aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@404: * Software engineering by Intevation GmbH aheinecke@404: * aheinecke@404: * This file is Free Software under the GNU GPL (v>=2) aheinecke@404: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@404: * See LICENSE.txt for details. aheinecke@404: */ aheinecke@4: #include "listutil.h" aheinecke@4: aheinecke@4: #include aheinecke@4: #include aheinecke@4: #include aheinecke@4: #include aheinecke@4: #include aheinecke@4: #include aheinecke@4: #include aheinecke@7: #include aheinecke@4: andre@1081: #ifdef WIN32 andre@1081: #include andre@1081: #endif andre@1081: aheinecke@286: #include "strhelp.h" andre@630: #include "logging.h" andre@1157: #include "portpath.h" aheinecke@286: andre@359: #ifdef RELEASE_BUILD aheinecke@28: #include "pubkey-release.h" aheinecke@28: #else aheinecke@28: #include "pubkey-test.h" aheinecke@28: #endif aheinecke@28: aheinecke@28: #pragma GCC diagnostic ignored "-Wconversion" aheinecke@28: /* Polarssl mh.h contains a conversion which gcc warns about */ aheinecke@28: #include aheinecke@31: #include aheinecke@31: #include aheinecke@28: #pragma GCC diagnostic pop aheinecke@28: bernhard@178: #define MAX_FILESIZE (MAX_LINE_LENGTH * MAX_LINES) aheinecke@4: aheinecke@31: #define READ_FILE_UNREADABLE -1 aheinecke@31: #define READ_FILE_TOO_LARGE -2 aheinecke@31: #define READ_FILE_NO_MEMORY -3 aheinecke@31: #define READ_FILE_READ_FAILED -4 aheinecke@86: #define READ_FILE_INVALID_CALL -5 andre@769: int andre@769: read_file(const char *file_name, char **data, size_t *size, andre@1081: const size_t max_size, FILE **fptr) aheinecke@4: { andre@905: FILE *f; andre@905: long file_size; aheinecke@86: andre@905: if (!file_name || !data || !size || !max_size) andre@905: { andre@905: return READ_FILE_INVALID_CALL; aheinecke@4: } andre@1157: andre@1157: f = port_fopen_rb(file_name, true); andre@905: if (f == NULL) andre@905: return READ_FILE_UNREADABLE; aheinecke@4: andre@905: fseek(f, 0, SEEK_END); andre@905: file_size = ftell(f); andre@905: if (file_size <= 0) andre@905: { andre@905: fclose(f); andre@905: return READ_FILE_UNREADABLE; aheinecke@38: } aheinecke@31: andre@905: fseek(f, 0, SEEK_SET); andre@905: andre@905: if (file_size + 1 == 0) andre@905: { andre@905: fclose(f); andre@905: return READ_FILE_TOO_LARGE; andre@905: } andre@905: *size = (size_t) file_size; andre@905: andre@905: if (*size > max_size) andre@905: { andre@905: fclose(f); andre@905: return READ_FILE_TOO_LARGE; aheinecke@4: } aheinecke@4: andre@905: *data = (char *) malloc( *size + 1 ); andre@905: if (*data == NULL) andre@905: { andre@905: fclose(f); andre@905: return READ_FILE_NO_MEMORY; aheinecke@4: } aheinecke@4: andre@905: if (fread(*data, 1, *size, f) != *size) andre@905: { andre@905: free(*data); andre@905: fclose(f); andre@905: return READ_FILE_READ_FAILED; andre@905: } aheinecke@4: andre@1081: if (fptr) andre@1081: { andre@1081: *fptr = f; andre@1081: } andre@1081: else andre@1081: { andre@1081: fclose(f); andre@1081: } aheinecke@4: andre@905: (*data)[*size] = '\0'; andre@905: andre@905: return 0; aheinecke@4: } aheinecke@4: aheinecke@68: int verify_list(const char *data, const size_t size) aheinecke@4: { andre@905: int ret = -1; andre@905: pk_context pub_key_ctx; andre@905: char *p; andre@770: andre@905: /* Modulus / 8 are the necessary bytes. */ andre@770: #ifndef TRUSTBRIDGE_RSA_KEY_SIZE andre@770: # error "Key size undefined" andre@770: #endif andre@905: const size_t sig_b64_size = TRUSTBRIDGE_RSA_KEY_SIZE / 8 * 4 / 3; andre@905: size_t sig_size = TRUSTBRIDGE_RSA_KEY_SIZE / 8; aheinecke@31: andre@905: char signature_b64[sig_b64_size + 1]; andre@905: unsigned char signature[sig_size]; andre@905: /* Hash algroithm is sha256 */ andre@905: unsigned char hash[32]; aheinecke@31: andre@905: if (!data || !size) andre@905: { andre@905: return -1; aheinecke@93: } aheinecke@93: andre@905: /* Fetch the signature from the first line od data */ andre@905: p = strchr(data, '\r'); andre@905: if (p == 0 || (unsigned int)(p - (data + 2)) != sig_b64_size) andre@905: { andre@905: /* printf("Invalid data. Signature might be too long.\n"); */ andre@905: return -1; aheinecke@31: } andre@905: strncpy(signature_b64, data + 2, sig_b64_size); andre@905: signature_b64[sig_b64_size] = '\0'; aheinecke@31: andre@905: ret = base64_decode(signature, &sig_size, andre@905: (unsigned char *)signature_b64, sig_b64_size); aheinecke@31: andre@905: if (ret != 0 || sig_size != TRUSTBRIDGE_RSA_KEY_SIZE / 8) andre@905: { andre@905: /* printf("failed to decode signature\n"); */ andre@905: return -1; aheinecke@31: } aheinecke@31: andre@905: /* Hash is calculated over the data without the first line. andre@905: * linebreaks are \r\n so the first char of the new line is andre@905: * p+2 */ andre@905: p += 2; andre@905: /* Size of the data to hash is the size - signature line andre@905: * signature line is sig_b64_size - "S:" and - "\r\n" so -4*/ andre@905: sha256((unsigned char *)p, size - sig_b64_size - 4, hash, 0); aheinecke@28: andre@905: pk_init(&pub_key_ctx); aheinecke@28: andre@905: ret = pk_parse_public_key(&pub_key_ctx, public_key_pem, andre@905: public_key_pem_size); andre@905: if (ret != 0) andre@905: { andre@905: ERRORPRINTF ("pk_parse_public_key failed with -0x%04x\n\n", -ret); andre@905: pk_free(&pub_key_ctx); andre@905: return ret; aheinecke@28: } aheinecke@28: andre@905: ret = pk_verify(&pub_key_ctx, POLARSSL_MD_SHA256, hash, 0, andre@905: signature, sig_size); aheinecke@31: andre@905: if (ret != 0) andre@905: { andre@905: ERRORPRINTF ("pk_verify failed with -0x%04x\n\n", -ret); aheinecke@31: } andre@905: pk_free(&pub_key_ctx); aheinecke@31: andre@905: return ret; aheinecke@28: } aheinecke@28: aheinecke@28: list_status_t read_and_verify_list(const char *file_name, char **data, aheinecke@28: size_t *size) aheinecke@28: { andre@905: list_status_t retval = UnknownError; andre@905: *data = NULL; andre@905: *size = 0; andre@905: int ret = 0; aheinecke@4: andre@1081: ret = read_file(file_name, data, size, MAX_FILESIZE, NULL); andre@905: andre@905: /* printf ("Ret: %i \n", ret); */ andre@905: if (ret != 0) andre@905: { andre@905: if (ret == READ_FILE_TOO_LARGE) andre@905: { andre@905: return TooLarge; aheinecke@31: } andre@905: if (ret == READ_FILE_UNREADABLE) andre@905: { andre@905: /* TODO: work with errno ? */ andre@905: /* errsv = errno; */ andre@905: /* perror("read_and_verify_list(), READ_FILE_UNREADABLE:"); */ andre@905: return SeekFailed; aheinecke@31: } andre@905: if (ret == READ_FILE_READ_FAILED) andre@905: { andre@905: /* TODO: work with ferror() or feof() ? */ andre@905: return ReadFailed; aheinecke@31: } andre@905: return UnknownError; aheinecke@4: } aheinecke@4: andre@905: if (!*data || !*size) andre@905: { andre@905: /* File is probably empty */ andre@905: return UnknownError; aheinecke@4: } aheinecke@4: andre@905: if (**data != 'S') andre@905: { andre@905: retval = InvalidFormat; andre@905: } andre@905: else andre@905: { andre@905: ret = verify_list (*data, *size); andre@905: if (ret == 0) andre@905: { andre@905: /* Hooray */ andre@905: return Valid; aheinecke@31: } andre@905: if (ret == -1) andre@905: { andre@905: /* our error */ andre@905: retval = InvalidFormat; andre@905: } andre@905: else andre@905: { andre@905: retval = InvalidSignature; aheinecke@31: } aheinecke@31: } aheinecke@9: andre@905: if (retval != Valid && *data) andre@905: { andre@905: free(*data); andre@905: *data = NULL; andre@905: *size = 0; aheinecke@4: } andre@905: return retval; aheinecke@4: } aheinecke@4: aheinecke@286: char ** aheinecke@286: get_certs_from_list (char *data, const size_t size) aheinecke@286: { aheinecke@286: char *cur = data; aheinecke@286: char **retval = NULL; aheinecke@68: aheinecke@286: if (!data || !size) aheinecke@286: { andre@626: ERRORPRINTF ("Invalid call to get_certs_to_remove \n"); aheinecke@286: return NULL; aheinecke@68: } aheinecke@286: aheinecke@286: while (cur) aheinecke@286: { aheinecke@286: char *next = strchr(cur, '\n'); aheinecke@286: if (strlen(cur) > 3 && (cur[0] == 'I' || cur[0] == 'R') && aheinecke@286: next - cur > 4) aheinecke@286: { aheinecke@292: size_t len = (size_t) (next - cur - 3); aheinecke@286: /* Remove I: or R: at the beginning and \r\n at the end */ aheinecke@286: strv_append(&retval, cur + 2, len); aheinecke@286: } aheinecke@286: cur = next ? (next + 1) : NULL; aheinecke@286: } aheinecke@286: return retval; aheinecke@68: } aheinecke@68: