/** * read the last serial number from file */ static chunk_t read_serial(void) { chunk_t hex, serial = chunk_empty; char one[] = {0x01}; FILE *fd; fd = fopen(OPENAC_SERIAL, "r"); if (fd) { hex = chunk_alloca(64); hex.len = fread(hex.ptr, 1, hex.len, fd); if (hex.len) { /* remove any terminating newline character */ if (hex.ptr[hex.len-1] == '\n') { hex.len--; } serial = chunk_alloca((hex.len / 2) + (hex.len % 2)); serial = chunk_from_hex(hex, serial.ptr); } fclose(fd); } else { DBG1(DBG_LIB, " file '%s' does not exist yet - serial number " "set to 01", OPENAC_SERIAL); } if (!serial.len) { return chunk_clone(chunk_create(one, 1)); } if (chunk_increment(serial)) { /* overflow, prepend 0x01 */ return chunk_cat("cc", chunk_create(one, 1), serial); } return chunk_clone(serial); }
/** * Determine the type of the attribute and its value */ static bool parse_attributes(char *name, char *value, value_type_t *value_type, configuration_attribute_type_t *type, configuration_attribute_type_t *type_ip6, chunk_t *blob) { host_t *addr = NULL, *mask = NULL; chunk_t addr_chunk, mask_chunk, blob_next; char *text = "", *pos_addr, *pos_mask, *pos_next, *endptr; int i; switch (*value_type) { case VALUE_STRING: *blob = chunk_create(value, strlen(value)); *blob = chunk_clone(*blob); break; case VALUE_HEX: *blob = chunk_from_hex(chunk_create(value, strlen(value)), NULL); break; case VALUE_ADDR: addr = host_create_from_string(value, 0); if (addr == NULL) { fprintf(stderr, "invalid IP address: '%s'.\n", value); return FALSE; } addr_chunk = addr->get_address(addr); *blob = chunk_clone(addr_chunk); break; case VALUE_SUBNET: *blob = chunk_empty; pos_next = value; do { pos_addr = pos_next; pos_next = strchr(pos_next, ','); if (pos_next) { *pos_next = '\0'; pos_next += 1; } pos_mask = strchr(pos_addr, '/'); if (pos_mask == NULL) { fprintf(stderr, "invalid IPv4 subnet: '%s'.\n", pos_addr); free(blob->ptr); return FALSE; } *pos_mask = '\0'; pos_mask += 1; addr = host_create_from_string(pos_addr, 0); mask = host_create_from_string(pos_mask, 0); if (addr == NULL || addr->get_family(addr) != AF_INET || mask == NULL || mask->get_family(addr) != AF_INET) { fprintf(stderr, "invalid IPv4 subnet: '%s/%s'.\n", pos_addr, pos_mask); DESTROY_IF(addr); DESTROY_IF(mask); free(blob->ptr); return FALSE; } addr_chunk = addr->get_address(addr); mask_chunk = mask->get_address(mask); blob_next = chunk_alloc(blob->len + UNITY_NETWORK_LEN); memcpy(blob_next.ptr, blob->ptr, blob->len); pos_addr = blob_next.ptr + blob->len; memset(pos_addr, 0x00, UNITY_NETWORK_LEN); memcpy(pos_addr, addr_chunk.ptr, 4); memcpy(pos_addr + 4, mask_chunk.ptr, 4); addr->destroy(addr); addr = NULL; mask->destroy(mask); chunk_free(blob); *blob = blob_next; } while (pos_next); break; case VALUE_NONE: *blob = chunk_empty; break; } /* init the attribute type */ *type = 0; *type_ip6 = 0; for (i = 0; i < countof(attr_info); i++) { if (strcaseeq(name, attr_info[i].keyword)) { *type = attr_info[i].type; *type_ip6 = attr_info[i].type_ip6; if (*value_type == VALUE_NONE) { *value_type = attr_info[i].value_type; return TRUE; } if (*value_type != attr_info[i].value_type && *value_type != VALUE_HEX) { switch (attr_info[i].value_type) { case VALUE_STRING: text = "a string"; break; case VALUE_HEX: text = "a hex"; break; case VALUE_ADDR: text = "an IP address"; break; case VALUE_SUBNET: text = "a subnet"; break; case VALUE_NONE: text = "no"; break; } fprintf(stderr, "the %s attribute requires %s value.\n", name, text); DESTROY_IF(addr); free(blob->ptr); return FALSE; } if (*value_type == VALUE_ADDR) { *type = (addr->get_family(addr) == AF_INET) ? attr_info[i].type : attr_info[i].type_ip6; addr->destroy(addr); } else if (*value_type == VALUE_HEX) { *value_type = attr_info[i].value_type; if (*value_type == VALUE_ADDR) { if (blob->len == 16) { *type = attr_info[i].type_ip6; } else if (blob->len != 4) { fprintf(stderr, "the %s attribute requires " "a valid IP address.\n", name); free(blob->ptr); return FALSE; } } } return TRUE; } } /* clean up */ DESTROY_IF(addr); /* is the attribute type numeric? */ *type = strtol(name, &endptr, 10); if (*endptr != '\0') { fprintf(stderr, "the %s attribute is not recognized.\n", name); free(blob->ptr); return FALSE; } if (*type < 1 || *type > 32767) { fprintf(stderr, "the attribute type must lie in the range 1..32767.\n"); free(blob->ptr); return FALSE; } if (*value_type == VALUE_NONE) { *value_type = VALUE_HEX; } return TRUE; }
static bool get_next_test_vector(test_vector_t *test) { param_t param = PARAM_UNKNOWN; char line[512]; memset(test, 0, sizeof(test_vector_t)); while (fgets(line, sizeof(line), ctx.in)) { enumerator_t *enumerator; chunk_t value = chunk_empty; char *token; int i; switch (line[0]) { case '\n': case '\r': case '#': case '\0': /* copy comments, empty lines etc. directly to the output */ if (param != PARAM_UNKNOWN) { /* seems we got a complete test vector */ return TRUE; } fputs(line, ctx.out); continue; case '[': /* control directives */ fputs(line, ctx.out); if (strpfx(line, "[ENCRYPT]")) { ctx.decrypt = FALSE; } else if (strpfx(line, "[DECRYPT]")) { ctx.decrypt = TRUE; } else if (strcasepfx(line, "[IVlen = ")) { ctx.ivlen = atoi(line + strlen("[IVlen = ")); } else if (strcasepfx(line, "[Taglen = ")) { ctx.icvlen = atoi(line + strlen("[Taglen = ")); } continue; default: /* we assume the rest of the lines are PARAM = VALUE pairs*/ fputs(line, ctx.out); break; } i = 0; enumerator = enumerator_create_token(line, "=", " \n\r"); while (enumerator->enumerate(enumerator, &token)) { switch (i++) { case 0: /* PARAM */ param = parse_parameter(token); continue; case 1: /* VALUE */ if (param != PARAM_UNKNOWN && param != PARAM_COUNT) { value = chunk_from_hex(chunk_from_str(token), NULL); } else { value = chunk_empty; } continue; default: break; } break; } enumerator->destroy(enumerator); if (i < 2) { value = chunk_empty; } switch (param) { case PARAM_KEY: test->key = value; break; case PARAM_IV: test->iv = value; test->external_iv = TRUE; break; case PARAM_PLAINTEXT: test->plain = value; break; case PARAM_CIPHERTEXT: test->cipher = value; break; case PARAM_AAD: test->aad = value; break; case PARAM_ICV: test->icv = value; break; default: chunk_free(&value); break; } } if (param != PARAM_UNKNOWN) { /* could be that the file ended with a complete test vector */ return TRUE; } return FALSE; }
END_TEST /******************************************************************************* * BASE16 encoding test */ START_TEST(test_base16) { /* test vectors from RFC 4648: * * BASE16("") = "" * BASE16("f") = "66" * BASE16("fo") = "666F" * BASE16("foo") = "666F6F" * BASE16("foob") = "666F6F62" * BASE16("fooba") = "666F6F6261" * BASE16("foobar") = "666F6F626172" */ typedef struct { bool upper; char *in; char *out; } testdata_t; testdata_t test[] = { {TRUE, "", ""}, {TRUE, "f", "66"}, {TRUE, "fo", "666F"}, {TRUE, "foo", "666F6F"}, {TRUE, "foob", "666F6F62"}, {TRUE, "fooba", "666F6F6261"}, {TRUE, "foobar", "666F6F626172"}, {FALSE, "", ""}, {FALSE, "f", "66"}, {FALSE, "fo", "666f"}, {FALSE, "foo", "666f6f"}, {FALSE, "foob", "666f6f62"}, {FALSE, "fooba", "666f6f6261"}, {FALSE, "foobar", "666f6f626172"}, }; testdata_t test_colon[] = { {TRUE, "", ""}, {TRUE, "f", "66"}, {TRUE, "fo", "66:6F"}, {TRUE, "foo", "66:6F:6F"}, {FALSE, "foob", "66:6f:6f:62"}, {FALSE, "fooba", "66:6f:6f:62:61"}, {FALSE, "foobar", "66:6f:6f:62:61:72"}, {FALSE, "foobar", "66:6f6f:6261:72"}, }; int i; for (i = 0; i < countof(test); i++) { chunk_t out; out = chunk_to_hex(chunk_create(test[i].in, strlen(test[i].in)), NULL, test[i].upper); ck_assert_str_eq(out.ptr, test[i].out); free(out.ptr); } for (i = 0; i < countof(test); i++) { chunk_t out; out = chunk_from_hex(chunk_create(test[i].out, strlen(test[i].out)), NULL); fail_unless(strneq(out.ptr, test[i].in, out.len), "base16 conversion error - should '%s', is %#B", test[i].in, &out); free(out.ptr); } for (i = 0; i < countof(test_colon); i++) { chunk_t out; out = chunk_from_hex(chunk_create(test_colon[i].out, strlen(test_colon[i].out)), NULL); fail_unless(strneq(out.ptr, test_colon[i].in, out.len), "base16 conversion error - should '%s', is %#B", test_colon[i].in, &out); free(out.ptr); } }
/** * Sign a CRL */ static int sign_crl() { cred_encoding_type_t form = CERT_ASN1_DER; private_key_t *private = NULL; public_key_t *public = NULL; certificate_t *ca = NULL, *crl = NULL; crl_t *lastcrl = NULL; x509_t *x509; hash_algorithm_t digest = HASH_UNKNOWN; signature_params_t *scheme = NULL; char *arg, *cacert = NULL, *cakey = NULL, *lastupdate = NULL, *error = NULL; char *basecrl = NULL; char serial[512], *keyid = NULL; int serial_len; crl_reason_t reason = CRL_REASON_UNSPECIFIED; time_t thisUpdate, nextUpdate, date = time(NULL); time_t lifetime = 15 * 24 * 60 * 60; char *datetu = NULL, *datenu = NULL, *dateform = NULL; linked_list_t *list, *cdps; enumerator_t *enumerator, *lastenum = NULL; x509_cdp_t *cdp; chunk_t crl_serial = chunk_empty, baseCrlNumber = chunk_empty; chunk_t encoding = chunk_empty; bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE, lib->ns); list = linked_list_create(); cdps = linked_list_create(); while (TRUE) { switch (command_getopt(&arg)) { case 'h': goto usage; case 'g': if (!enum_from_name(hash_algorithm_short_names, arg, &digest)) { error = "invalid --digest type"; goto usage; } continue; case 'R': if (streq(arg, "pss")) { pss = TRUE; } else if (!streq(arg, "pkcs1")) { error = "invalid RSA padding"; goto usage; } continue; case 'c': cacert = arg; continue; case 'k': cakey = arg; continue; case 'x': keyid = arg; continue; case 'a': lastupdate = arg; continue; case 'l': lifetime = atoi(arg) * 24 * 60 * 60; if (!lifetime) { error = "invalid --lifetime value"; goto usage; } continue; case 'D': dateform = arg; continue; case 'F': datetu = arg; continue; case 'T': datenu = arg; continue; case 'z': serial_len = read_serial(arg, serial, sizeof(serial)); if (serial_len < 0) { snprintf(serial, sizeof(serial), "parsing certificate '%s' failed", arg); error = serial; goto error; } add_revoked(list, chunk_create(serial, serial_len), reason, date); date = time(NULL); reason = CRL_REASON_UNSPECIFIED; continue; case 's': { chunk_t chunk; int hex_len; hex_len = strlen(arg); if ((hex_len / 2) + (hex_len % 2) > sizeof(serial)) { error = "invalid serial"; goto usage; } chunk = chunk_from_hex(chunk_create(arg, hex_len), serial); serial_len = chunk.len; add_revoked(list, chunk_create(serial, serial_len), reason, date); date = time(NULL); reason = CRL_REASON_UNSPECIFIED; continue; } case 'b': basecrl = arg; continue; case 'u': INIT(cdp, .uri = strdup(arg), ); cdps->insert_last(cdps, cdp); continue; case 'r': if (streq(arg, "key-compromise")) { reason = CRL_REASON_KEY_COMPROMISE; } else if (streq(arg, "ca-compromise")) { reason = CRL_REASON_CA_COMPROMISE; } else if (streq(arg, "affiliation-changed")) { reason = CRL_REASON_AFFILIATION_CHANGED; } else if (streq(arg, "superseded")) { reason = CRL_REASON_SUPERSEDED; } else if (streq(arg, "cessation-of-operation")) { reason = CRL_REASON_CESSATION_OF_OPERATON; } else if (streq(arg, "certificate-hold")) { reason = CRL_REASON_CERTIFICATE_HOLD; } else { error = "invalid revocation reason"; goto usage; } continue; case 'd': date = atol(arg); if (!date) { error = "invalid date"; goto usage; } continue; case 'f': if (!get_form(arg, &form, CRED_CERTIFICATE)) { error = "invalid output format"; goto usage; } continue; case EOF: break; default: error = "invalid --signcrl option"; goto usage; } break; } if (!cacert) { error = "--cacert is required"; goto usage; } if (!cakey && !keyid) { error = "--cakey or --keyid is required"; goto usage; } if (!calculate_lifetime(dateform, datetu, datenu, lifetime, &thisUpdate, &nextUpdate)) { error = "invalid --this/next-update datetime"; goto usage; } ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, cacert, BUILD_END); if (!ca) { error = "parsing CA certificate failed"; goto error; } x509 = (x509_t*)ca; if (!(x509->get_flags(x509) & (X509_CA | X509_CRL_SIGN))) { error = "CA certificate misses CA basicConstraint / CRLSign keyUsage"; goto error; } public = ca->get_public_key(ca);
/** * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934) */ static status_t pem_to_bin(chunk_t *blob, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = chunk_empty; chunk_t iv = chunk_empty; u_char iv_buf[HASH_SIZE_MD5]; status_t status = NOT_FOUND; enumerator_t *enumerator; shared_key_t *shared; dst.len = 0; iv.ptr = iv_buf; iv.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = PEM_HEADER; if (memchr(line.ptr, ':', line.len) == NULL) { state = PEM_BODY; } } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = chunk_empty; chunk_t value = chunk_empty; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ DBG2(DBG_ASN, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) { continue; } if (match("Proc-Type", &name) && *value.ptr == '4') { encrypted = TRUE; } else if (match("DEK-Info", &name)) { chunk_t dek; if (!extract_token(&dek, ',', &value)) { dek = value; } if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { DBG1(DBG_ASN, " encryption algorithm '%.*s'" " not supported", (int)dek.len, dek.ptr); return NOT_SUPPORTED; } if (!eat_whitespace(&value) || value.len > 2*sizeof(iv_buf)) { return PARSE_ERROR; } iv = chunk_from_hex(value, iv_buf); } } else /* state is PEM_BODY */ { chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; DBG2(DBG_ASN, " armor checksum: %.*s", (int)data.len, data.ptr); continue; } if (blob->len - dst.len < data.len / 4 * 3) { state = PEM_ABORT; } data = chunk_from_base64(data, dst.ptr); dst.ptr += data.len; dst.len += data.len; } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) { DBG1(DBG_LIB, " file coded in unknown format, discarded"); return PARSE_ERROR; } if (!encrypted) { return SUCCESS; } enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) { chunk_t passphrase, chunk; passphrase = shared->get_key(shared); chunk = chunk_clone(*blob); status = pem_decrypt(&chunk, alg, key_size, iv, passphrase); if (status == SUCCESS) { memcpy(blob->ptr, chunk.ptr, chunk.len); blob->len = chunk.len; } free(chunk.ptr); if (status != INVALID_ARG) { /* try again only if passphrase invalid */ break; } } enumerator->destroy(enumerator); return status; }
/** * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934) */ static status_t pem_to_bin(chunk_t *blob, chunk_t(*cb)(void*,int), void *cb_data, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = chunk_empty; chunk_t iv = chunk_empty; chunk_t passphrase; int try = 0; u_char iv_buf[HASH_SIZE_MD5]; dst.len = 0; iv.ptr = iv_buf; iv.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = PEM_HEADER; if (memchr(line.ptr, ':', line.len) == NULL) { state = PEM_BODY; } } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = chunk_empty; chunk_t value = chunk_empty; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ DBG2(DBG_LIB, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) { continue; } if (match("Proc-Type", &name) && *value.ptr == '4') { encrypted = TRUE; } else if (match("DEK-Info", &name)) { chunk_t dek; if (!extract_token(&dek, ',', &value)) { dek = value; } if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { DBG1(DBG_LIB, " encryption algorithm '%.*s'" " not supported", dek.len, dek.ptr); return NOT_SUPPORTED; } eat_whitespace(&value); iv = chunk_from_hex(value, iv.ptr); } } else /* state is PEM_BODY */ { chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; DBG2(DBG_LIB, " armor checksum: %.*s", (int)data.len, data.ptr); continue; } if (blob->len - dst.len < data.len / 4 * 3) { state = PEM_ABORT; } data = chunk_from_base64(data, dst.ptr); dst.ptr += data.len; dst.len += data.len; } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) { DBG1(DBG_LIB, " file coded in unknown format, discarded"); return PARSE_ERROR; } if (!encrypted) { return SUCCESS; } if (!cb) { DBG1(DBG_LIB, " missing passphrase"); return INVALID_ARG; } while (TRUE) { passphrase = cb(cb_data, ++try); if (!passphrase.len || !passphrase.ptr) { return INVALID_ARG; } switch (pem_decrypt(blob, alg, key_size, iv, passphrase)) { case INVALID_ARG: /* bad passphrase, retry */ continue; case SUCCESS: return SUCCESS; default: return FAILED; } } }