diff options
Diffstat (limited to 'source4/heimdal/lib')
50 files changed, 936 insertions, 484 deletions
diff --git a/source4/heimdal/lib/asn1/asn1parse.y b/source4/heimdal/lib/asn1/asn1parse.y index 7975fe4f6b..3835744bbd 100644 --- a/source4/heimdal/lib/asn1/asn1parse.y +++ b/source4/heimdal/lib/asn1/asn1parse.y @@ -687,6 +687,11 @@ RestrictedCharactedStringType: kw_GeneralString $$ = new_tag(ASN1_C_UNIV, UT_GeneralString, TE_EXPLICIT, new_type(TGeneralString)); } + | kw_TeletexString + { + $$ = new_tag(ASN1_C_UNIV, UT_TeletexString, + TE_EXPLICIT, new_type(TTeletexString)); + } | kw_UTF8String { $$ = new_tag(ASN1_C_UNIV, UT_UTF8String, diff --git a/source4/heimdal/lib/asn1/der_get.c b/source4/heimdal/lib/asn1/der_get.c index aee565040f..5a062fb339 100644 --- a/source4/heimdal/lib/asn1/der_get.c +++ b/source4/heimdal/lib/asn1/der_get.c @@ -305,7 +305,7 @@ der_get_octet_string_ber (const unsigned char *p, size_t len, void *ptr; ptr = realloc(data->data, data->length + datalen); - if (ptr == NULL) { + if (ptr == NULL && data->length + datalen != 0) { e = ENOMEM; goto out; } @@ -354,21 +354,23 @@ der_get_heim_integer (const unsigned char *p, size_t len, p++; data->length--; } - data->data = malloc(data->length); - if (data->data == NULL) { - data->length = 0; - if (size) - *size = 0; - return ENOMEM; - } - q = &((unsigned char*)data->data)[data->length - 1]; - p += data->length - 1; - while (q >= (unsigned char*)data->data) { - *q = *p ^ 0xff; - if (carry) - carry = !++*q; - p--; - q--; + if (data->length) { + data->data = malloc(data->length); + if (data->data == NULL) { + data->length = 0; + if (size) + *size = 0; + return ENOMEM; + } + q = &((unsigned char*)data->data)[data->length - 1]; + p += data->length - 1; + while (q >= (unsigned char*)data->data) { + *q = *p ^ 0xff; + if (carry) + carry = !++*q; + p--; + q--; + } } } else { data->negative = 0; diff --git a/source4/heimdal/lib/asn1/gen.c b/source4/heimdal/lib/asn1/gen.c index e156c7cefb..780c18b36f 100644 --- a/source4/heimdal/lib/asn1/gen.c +++ b/source4/heimdal/lib/asn1/gen.c @@ -494,6 +494,9 @@ define_asn1 (int level, Type *t) case TGeneralString: fprintf (headerfile, "GeneralString"); break; + case TTeletexString: + fprintf (headerfile, "TeletexString"); + break; case TTag: { const char *classnames[] = { "UNIVERSAL ", "APPLICATION ", "" /* CONTEXT */, "PRIVATE " }; @@ -685,6 +688,10 @@ define_type (int level, const char *name, Type *t, int typedefp, int preservep) space(level); fprintf (headerfile, "heim_general_string %s;\n", name); break; + case TTeletexString: + space(level); + fprintf (headerfile, "heim_general_string %s;\n", name); + break; case TTag: define_type (level, name, t->subtype, typedefp, preservep); break; diff --git a/source4/heimdal/lib/asn1/gen_copy.c b/source4/heimdal/lib/asn1/gen_copy.c index 5042ed64ed..f28647d19a 100644 --- a/source4/heimdal/lib/asn1/gen_copy.c +++ b/source4/heimdal/lib/asn1/gen_copy.c @@ -184,6 +184,9 @@ copy_type (const char *from, const char *to, const Type *t, int preserve) case TGeneralString: copy_primitive ("general_string", from, to); break; + case TTeletexString: + copy_primitive ("general_string", from, to); + break; case TUTCTime: fprintf(codefile, "*(%s) = *(%s);\n", to, from); break; diff --git a/source4/heimdal/lib/asn1/gen_decode.c b/source4/heimdal/lib/asn1/gen_decode.c index cf7f0b05dc..327de4c98c 100644 --- a/source4/heimdal/lib/asn1/gen_decode.c +++ b/source4/heimdal/lib/asn1/gen_decode.c @@ -67,6 +67,7 @@ is_primitive_type(int type) case TEnumerated: case TGeneralizedTime: case TGeneralString: + case TTeletexString: case TOID: case TUTCTime: case TUTF8String: @@ -109,6 +110,11 @@ find_tag (const Type *t, *ty = PRIM; *tag = UT_GeneralString; break; + case TTeletexString: + *cl = ASN1_C_UNIV; + *ty = PRIM; + *tag = UT_TeletexString; + break; case TGeneralizedTime: *cl = ASN1_C_UNIV; *ty = PRIM; @@ -489,6 +495,9 @@ decode_type (const char *name, const Type *t, int optional, case TGeneralString: decode_primitive ("general_string", name, forwstr); break; + case TTeletexString: + decode_primitive ("general_string", name, forwstr); + break; case TTag:{ char *tname, *typestring; char *ide = NULL; @@ -621,7 +630,7 @@ decode_type (const char *name, const Type *t, int optional, fprintf(codefile, "else {\n" "(%s)->u.%s.data = calloc(1, len);\n" - "if ((%s)->u.%s.data == NULL) {\n" + "if ((%s)->u.%s.data == NULL && len != 0) {\n" "e = ENOMEM; %s;\n" "}\n" "(%s)->u.%s.length = len;\n" @@ -703,6 +712,7 @@ generate_type_decode (const Symbol *s) case TOID: case TGeneralizedTime: case TGeneralString: + case TTeletexString: case TUTF8String: case TPrintableString: case TIA5String: @@ -734,7 +744,7 @@ generate_type_decode (const Symbol *s) if (preserve) fprintf (codefile, "data->_save.data = calloc(1, ret);\n" - "if (data->_save.data == NULL) { \n" + "if (data->_save.data == NULL && ret != 0) { \n" "e = ENOMEM; goto fail; \n" "}\n" "data->_save.length = ret;\n" diff --git a/source4/heimdal/lib/asn1/gen_encode.c b/source4/heimdal/lib/asn1/gen_encode.c index 1f8078a0ee..012d4677f4 100644 --- a/source4/heimdal/lib/asn1/gen_encode.c +++ b/source4/heimdal/lib/asn1/gen_encode.c @@ -383,6 +383,10 @@ encode_type (const char *name, const Type *t, const char *tmpstr) encode_primitive ("general_string", name); constructed = 0; break; + case TTeletexString: + encode_primitive ("general_string", name); + constructed = 0; + break; case TTag: { char *tname; int c; @@ -521,6 +525,7 @@ generate_type_encode (const Symbol *s) case TOctetString: case TGeneralizedTime: case TGeneralString: + case TTeletexString: case TUTCTime: case TUTF8String: case TPrintableString: diff --git a/source4/heimdal/lib/asn1/gen_free.c b/source4/heimdal/lib/asn1/gen_free.c index fac1f6da5d..48fe8cd787 100644 --- a/source4/heimdal/lib/asn1/gen_free.c +++ b/source4/heimdal/lib/asn1/gen_free.c @@ -145,6 +145,9 @@ free_type (const char *name, const Type *t, int preserve) case TGeneralString: free_primitive ("general_string", name); break; + case TTeletexString: + free_primitive ("general_string", name); + break; case TUTF8String: free_primitive ("utf8string", name); break; diff --git a/source4/heimdal/lib/asn1/gen_length.c b/source4/heimdal/lib/asn1/gen_length.c index 7f9755e2da..e1f045c4c5 100644 --- a/source4/heimdal/lib/asn1/gen_length.c +++ b/source4/heimdal/lib/asn1/gen_length.c @@ -219,6 +219,9 @@ length_type (const char *name, const Type *t, case TGeneralString: length_primitive ("general_string", name, variable); break; + case TTeletexString: + length_primitive ("general_string", name, variable); + break; case TUTCTime: length_primitive ("utctime", name, variable); break; diff --git a/source4/heimdal/lib/asn1/rfc2459.asn1 b/source4/heimdal/lib/asn1/rfc2459.asn1 index 51cac55cc0..9794ca1514 100644 --- a/source4/heimdal/lib/asn1/rfc2459.asn1 +++ b/source4/heimdal/lib/asn1/rfc2459.asn1 @@ -150,11 +150,9 @@ AttributeType ::= OBJECT IDENTIFIER AttributeValue ::= heim_any -TeletexStringx ::= [UNIVERSAL 20] IMPLICIT OCTET STRING - DirectoryString ::= CHOICE { ia5String IA5String, - teletexString TeletexStringx, + teletexString TeletexString, printableString PrintableString, universalString UniversalString, utf8String UTF8String, diff --git a/source4/heimdal/lib/asn1/symbol.h b/source4/heimdal/lib/asn1/symbol.h index 3a0f807980..a39c8f4651 100644 --- a/source4/heimdal/lib/asn1/symbol.h +++ b/source4/heimdal/lib/asn1/symbol.h @@ -44,6 +44,7 @@ enum typetype { TChoice, TEnumerated, TGeneralString, + TTeletexString, TGeneralizedTime, TIA5String, TInteger, diff --git a/source4/heimdal/lib/com_err/com_right.h b/source4/heimdal/lib/com_err/com_right.h index 46aec001ad..e13855abad 100644 --- a/source4/heimdal/lib/com_err/com_right.h +++ b/source4/heimdal/lib/com_err/com_right.h @@ -52,6 +52,7 @@ struct et_list { extern struct et_list *_et_list; const char *com_right (struct et_list *list, long code); +const char *com_right_r (struct et_list *list, long code, char *, size_t); void initialize_error_table_r (struct et_list **, const char **, int, long); void free_error_table (struct et_list *); diff --git a/source4/heimdal/lib/com_err/error.c b/source4/heimdal/lib/com_err/error.c index 6b12c00c7a..d4a42ac5de 100644 --- a/source4/heimdal/lib/com_err/error.c +++ b/source4/heimdal/lib/com_err/error.c @@ -49,16 +49,26 @@ const char * com_right(struct et_list *list, long code) { struct et_list *p; + for (p = list; p; p = p->next) + if (code >= p->table->base && code < p->table->base + p->table->n_msgs) + return p->table->msgs[code - p->table->base]; + return NULL; +} + +const char * +com_right_r(struct et_list *list, long code, char *str, size_t len) +{ + struct et_list *p; for (p = list; p; p = p->next) { if (code >= p->table->base && code < p->table->base + p->table->n_msgs) { - const char *str = p->table->msgs[code - p->table->base]; + const char *msg = p->table->msgs[code - p->table->base]; #ifdef LIBINTL char domain[12 + 20]; snprintf(domain, sizeof(domain), "heim_com_err%d", p->table->base); #endif - return dgettext(domain, str); + strlcpy(str, dgettext(domain, msg), len); + return str; } - } return NULL; } diff --git a/source4/heimdal/lib/gssapi/krb5/init_sec_context.c b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c index 1954c101c7..7f84efe354 100644 --- a/source4/heimdal/lib/gssapi/krb5/init_sec_context.c +++ b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c @@ -297,13 +297,12 @@ do_delegation (krb5_context context, if (kret) goto out; - kret = krb5_build_principal(context, - &creds.server, - strlen(creds.client->realm), - creds.client->realm, - KRB5_TGS_NAME, - creds.client->realm, - NULL); + kret = krb5_make_principal(context, + &creds.server, + creds.client->realm, + KRB5_TGS_NAME, + creds.client->realm, + NULL); if (kret) goto out; @@ -610,12 +609,11 @@ init_auth_restart krb5_set_kdc_sec_offset (context, offset, -1); } - kret = krb5_build_authenticator (context, + kret = _krb5_build_authenticator(context, ctx->auth_context, enctype, ctx->kcred, &cksum, - NULL, &authenticator, KRB5_KU_AP_REQ_AUTH); diff --git a/source4/heimdal/lib/hcrypto/evp-cc.c b/source4/heimdal/lib/hcrypto/evp-cc.c index 1bf8ca8af9..15b3479f8e 100644 --- a/source4/heimdal/lib/hcrypto/evp-cc.c +++ b/source4/heimdal/lib/hcrypto/evp-cc.c @@ -296,6 +296,7 @@ EVP_cc_aes_256_cbc(void) * */ +#ifdef COMMONCRYPTO_SUPPORTS_RC2 static int cc_rc2_cbc_init(EVP_CIPHER_CTX *ctx, const unsigned char * key, @@ -305,6 +306,7 @@ cc_rc2_cbc_init(EVP_CIPHER_CTX *ctx, struct cc_key *cc = ctx->cipher_data; return init_cc_key(encp, kCCAlgorithmRC2, key, ctx->cipher->key_len, iv, &cc->href); } +#endif /** * The RC2 cipher type - common crypto @@ -318,6 +320,7 @@ cc_rc2_cbc_init(EVP_CIPHER_CTX *ctx, const EVP_CIPHER * EVP_cc_rc2_cbc(void) { +#ifdef COMMONCRYPTO_SUPPORTS_RC2 static const EVP_CIPHER rc2_cbc = { 0, kCCBlockSizeRC2, @@ -334,6 +337,9 @@ EVP_cc_rc2_cbc(void) NULL }; return &rc2_cbc; +#else + return NULL; +#endif } /** @@ -348,6 +354,7 @@ EVP_cc_rc2_cbc(void) const EVP_CIPHER * EVP_cc_rc2_40_cbc(void) { +#ifdef COMMONCRYPTO_SUPPORTS_RC2 static const EVP_CIPHER rc2_40_cbc = { 0, kCCBlockSizeRC2, @@ -364,6 +371,9 @@ EVP_cc_rc2_40_cbc(void) NULL }; return &rc2_40_cbc; +#else + return NULL; +#endif } @@ -379,6 +389,7 @@ EVP_cc_rc2_40_cbc(void) const EVP_CIPHER * EVP_cc_rc2_64_cbc(void) { +#ifdef COMMONCRYPTO_SUPPORTS_RC2 static const EVP_CIPHER rc2_64_cbc = { 0, kCCBlockSizeRC2, @@ -395,6 +406,9 @@ EVP_cc_rc2_64_cbc(void) NULL }; return &rc2_64_cbc; +#else + return NULL; +#endif } /** diff --git a/source4/heimdal/lib/hcrypto/hmac.c b/source4/heimdal/lib/hcrypto/hmac.c index 282dc38113..dcd836d0be 100644 --- a/source4/heimdal/lib/hcrypto/hmac.c +++ b/source4/heimdal/lib/hcrypto/hmac.c @@ -121,7 +121,8 @@ HMAC_Init_ex(HMAC_CTX *ctx, for (i = 0, p = ctx->opad; i < keylen; i++) p[i] ^= ((const unsigned char *)key)[i]; - ctx->ctx = EVP_MD_CTX_create(); + if (ctx->ctx == NULL) + ctx->ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx->ctx, ctx->md, ctx->engine); EVP_DigestUpdate(ctx->ctx, ctx->ipad, EVP_MD_block_size(ctx->md)); diff --git a/source4/heimdal/lib/hcrypto/rand-unix.c b/source4/heimdal/lib/hcrypto/rand-unix.c index 2bfa265fa4..fcad39f1de 100644 --- a/source4/heimdal/lib/hcrypto/rand-unix.c +++ b/source4/heimdal/lib/hcrypto/rand-unix.c @@ -95,8 +95,10 @@ unix_bytes(unsigned char *outdata, int size) ssize_t count; int once = 0; - if (size <= 0) + if (size < 0) return 0; + else if (size == 0) + return 1; HEIMDAL_MUTEX_lock(&random_mutex); if (random_fd == -1) { diff --git a/source4/heimdal/lib/hdb/ext.c b/source4/heimdal/lib/hdb/ext.c index 8248098dc5..a8a882c6b2 100644 --- a/source4/heimdal/lib/hdb/ext.c +++ b/source4/heimdal/lib/hdb/ext.c @@ -281,12 +281,11 @@ hdb_entry_get_password(krb5_context context, HDB *db, const hdb_entry *entry, char **p) { HDB_extension *ext; - char *str; int ret; ext = hdb_find_extension(entry, choice_HDB_extension_data_password); if (ext) { - heim_utf8_string str2; + heim_utf8_string str; heim_octet_string pw; if (db->hdb_master_key_set && ext->data.u.password.mkvno) { @@ -314,13 +313,13 @@ hdb_entry_get_password(krb5_context context, HDB *db, return ret; } - str2 = pw.data; - if (str2[pw.length - 1] != '\0') { + str = pw.data; + if (str[pw.length - 1] != '\0') { krb5_set_error_message(context, EINVAL, "password malformated"); return EINVAL; } - *p = strdup(str2); + *p = strdup(str); der_free_octet_string(&pw); if (*p == NULL) { @@ -330,14 +329,17 @@ hdb_entry_get_password(krb5_context context, HDB *db, return 0; } - ret = krb5_unparse_name(context, entry->principal, &str); - if (ret == 0) { - krb5_set_error_message(context, ENOENT, "no password attributefor %s", str); - free(str); - } else - krb5_clear_error_message(context); - - return ENOENT; + { + char *name; + ret = krb5_unparse_name(context, entry->principal, &name); + if (ret == 0) { + krb5_set_error_message(context, ENOENT, "no password attributefor %s", name); + free(name); + } else + krb5_clear_error_message(context); + + return ENOENT; + } } int diff --git a/source4/heimdal/lib/hdb/hdb.c b/source4/heimdal/lib/hdb/hdb.c index c5d91b8f9d..fa70c7778d 100644 --- a/source4/heimdal/lib/hdb/hdb.c +++ b/source4/heimdal/lib/hdb/hdb.c @@ -59,7 +59,7 @@ * */ - +const int hdb_interface_version = HDB_INTERFACE_VERSION; static struct hdb_method methods[] = { #if HAVE_DB1 || HAVE_DB3 diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h index 8eba864fd3..f34c9fb36e 100644 --- a/source4/heimdal/lib/hdb/hdb.h +++ b/source4/heimdal/lib/hdb/hdb.h @@ -53,6 +53,7 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK }; #define HDB_F_GET_KRBTGT 16 /* fetch krbtgt */ #define HDB_F_GET_ANY 28 /* fetch any of client,server,krbtgt */ #define HDB_F_CANON 32 /* want canonicalition */ +#define HDB_F_ADMIN_DATA 64 /* want data that kdc don't use */ /* hdb_capability_flags */ #define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1 @@ -245,6 +246,8 @@ struct hdb_method { krb5_error_code (*create)(krb5_context, HDB **, const char *filename); }; +extern const int hdb_interface_version; + #include <hdb-protos.h> #endif /* __HDB_H__ */ diff --git a/source4/heimdal/lib/hx509/ca.c b/source4/heimdal/lib/hx509/ca.c index 552a869809..8ec6eae22a 100644 --- a/source4/heimdal/lib/hx509/ca.c +++ b/source4/heimdal/lib/hx509/ca.c @@ -692,7 +692,7 @@ add_utf8_san(hx509_context context, const heim_oid *oid, const char *string) { - const PKIXXmppAddr ustring = string; + const PKIXXmppAddr ustring = (const PKIXXmppAddr)string; heim_octet_string os; size_t size; int ret; diff --git a/source4/heimdal/lib/hx509/cert.c b/source4/heimdal/lib/hx509/cert.c index 7eaf6eb3c8..ebf02a99e3 100644 --- a/source4/heimdal/lib/hx509/cert.c +++ b/source4/heimdal/lib/hx509/cert.c @@ -283,6 +283,7 @@ hx509_cert_init_data(hx509_context context, return ret; } if (size != len) { + free_Certificate(&t); hx509_set_error_string(context, 0, HX509_EXTRA_DATA_AFTER_STRUCTURE, "Extra data after certificate"); return HX509_EXTRA_DATA_AFTER_STRUCTURE; @@ -445,7 +446,7 @@ hx509_verify_attach_anchors(hx509_verify_ctx ctx, hx509_certs set) { if (ctx->trust_anchors) hx509_certs_free(&ctx->trust_anchors); - ctx->trust_anchors = _hx509_certs_ref(set); + ctx->trust_anchors = hx509_certs_ref(set); } /** @@ -1926,9 +1927,9 @@ hx509_verify_path(hx509_context context, * */ if (ctx->trust_anchors) - anchors = _hx509_certs_ref(ctx->trust_anchors); + anchors = hx509_certs_ref(ctx->trust_anchors); else if (context->default_trust_anchors && ALLOW_DEF_TA(ctx)) - anchors = _hx509_certs_ref(context->default_trust_anchors); + anchors = hx509_certs_ref(context->default_trust_anchors); else { ret = hx509_certs_init(context, "MEMORY:no-TA", 0, NULL, &anchors); if (ret) @@ -3451,3 +3452,66 @@ out: hx509_env_free(&envcert); return ret; } + +/** + * Print a simple representation of a certificate + * + * @param context A hx509 context, can be NULL + * @param cert certificate to print + * @param out the stdio output stream, if NULL, stdout is used + * + * @return An hx509 error code + * + * @ingroup hx509_cert + */ + +int +hx509_print_cert(hx509_context context, hx509_cert cert, FILE *out) +{ + hx509_name name; + char *str; + int ret; + + if (out == NULL) + out = stderr; + + ret = hx509_cert_get_issuer(cert, &name); + if (ret) + return ret; + hx509_name_to_string(name, &str); + hx509_name_free(&name); + fprintf(out, " issuer: \"%s\"\n", str); + free(str); + + ret = hx509_cert_get_subject(cert, &name); + if (ret) + return ret; + hx509_name_to_string(name, &str); + hx509_name_free(&name); + fprintf(out, " subject: \"%s\"\n", str); + free(str); + + { + heim_integer serialNumber; + + ret = hx509_cert_get_serialnumber(cert, &serialNumber); + if (ret) + return ret; + ret = der_print_hex_heim_integer(&serialNumber, &str); + if (ret) + return ret; + der_free_heim_integer(&serialNumber); + fprintf(out, " serial: %s\n", str); + free(str); + } + + printf(" keyusage: "); + ret = hx509_cert_keyusage_print(context, cert, &str); + if (ret == 0) { + fprintf(out, "%s\n", str); + free(str); + } else + fprintf(out, "no"); + + return 0; +} diff --git a/source4/heimdal/lib/hx509/error.c b/source4/heimdal/lib/hx509/error.c index 45813efb38..fc3cf90b32 100644 --- a/source4/heimdal/lib/hx509/error.c +++ b/source4/heimdal/lib/hx509/error.c @@ -67,8 +67,10 @@ free_error_string(hx509_error msg) void hx509_clear_error_string(hx509_context context) { - free_error_string(context->error); - context->error = NULL; + if (context) { + free_error_string(context->error); + context->error = NULL; + } } /** @@ -91,6 +93,9 @@ hx509_set_error_stringv(hx509_context context, int flags, int code, { hx509_error msg; + if (context == NULL) + return; + msg = calloc(1, sizeof(*msg)); if (msg == NULL) { hx509_clear_error_string(context); diff --git a/source4/heimdal/lib/hx509/file.c b/source4/heimdal/lib/hx509/file.c index 674d2706ce..56e25766ef 100644 --- a/source4/heimdal/lib/hx509/file.c +++ b/source4/heimdal/lib/hx509/file.c @@ -66,7 +66,7 @@ _hx509_write_file(const char *fn, const void *data, size_t length) */ static void -header(FILE *f, const char *type, const char *str) +print_pem_stamp(FILE *f, const char *type, const char *str) { fprintf(f, "-----%s %s-----\n", type, str); } @@ -82,7 +82,7 @@ hx509_pem_write(hx509_context context, const char *type, #define ENCODE_LINE_LENGTH 54 - header(f, "BEGIN", type); + print_pem_stamp(f, "BEGIN", type); while (headers) { fprintf(f, "%s: %s\n%s", @@ -110,7 +110,7 @@ hx509_pem_write(hx509_context context, const char *type, free(line); } - header(f, "END", type); + print_pem_stamp(f, "END", type); return 0; } @@ -121,14 +121,14 @@ hx509_pem_write(hx509_context context, const char *type, int hx509_pem_add_header(hx509_pem_header **headers, - const char *hdr, const char *value) + const char *header, const char *value) { hx509_pem_header *h; h = calloc(1, sizeof(*h)); if (h == NULL) return ENOMEM; - h->header = strdup(hdr); + h->header = strdup(header); if (h->header == NULL) { free(h); return ENOMEM; @@ -164,10 +164,10 @@ hx509_pem_free_header(hx509_pem_header *headers) */ const char * -hx509_pem_find_header(const hx509_pem_header *h, const char *hdr) +hx509_pem_find_header(const hx509_pem_header *h, const char *header) { while(h) { - if (strcmp(hdr, h->header) == 0) + if (strcmp(header, h->header) == 0) return h->value; h = h->next; } diff --git a/source4/heimdal/lib/hx509/keyset.c b/source4/heimdal/lib/hx509/keyset.c index c4f035ab87..4a96cff530 100644 --- a/source4/heimdal/lib/hx509/keyset.c +++ b/source4/heimdal/lib/hx509/keyset.c @@ -198,7 +198,7 @@ hx509_certs_store(hx509_context context, hx509_certs -_hx509_certs_ref(hx509_certs certs) +hx509_certs_ref(hx509_certs certs) { if (certs == NULL) return NULL; diff --git a/source4/heimdal/lib/hx509/ks_file.c b/source4/heimdal/lib/hx509/ks_file.c index 3955820aef..f137b84641 100644 --- a/source4/heimdal/lib/hx509/ks_file.c +++ b/source4/heimdal/lib/hx509/ks_file.c @@ -367,7 +367,7 @@ file_init_common(hx509_context context, const char *residue, hx509_lock lock, outformat format) { char *p, *pnext; - struct ks_file *f = NULL; + struct ks_file *ksf = NULL; hx509_private_key *keys = NULL; int ret; struct pem_ctx pem_ctx; @@ -380,15 +380,15 @@ file_init_common(hx509_context context, if (lock == NULL) lock = _hx509_empty_lock; - f = calloc(1, sizeof(*f)); - if (f == NULL) { + ksf = calloc(1, sizeof(*ksf)); + if (ksf == NULL) { hx509_clear_error_string(context); return ENOMEM; } - f->format = format; + ksf->format = format; - f->fn = strdup(residue); - if (f->fn == NULL) { + ksf->fn = strdup(residue); + if (ksf->fn == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; @@ -401,10 +401,10 @@ file_init_common(hx509_context context, if (flags & HX509_CERTS_CREATE) { ret = hx509_certs_init(context, "MEMORY:ks-file-create", - 0, lock, &f->certs); + 0, lock, &ksf->certs); if (ret) goto out; - *data = f; + *data = ksf; return 0; } @@ -412,25 +412,25 @@ file_init_common(hx509_context context, if (ret) goto out; - for (p = f->fn; p != NULL; p = pnext) { - FILE *f2; + for (p = ksf->fn; p != NULL; p = pnext) { + FILE *f; pnext = strchr(p, ','); if (pnext) *pnext++ = '\0'; - if ((f2 = fopen(p, "r")) == NULL) { + if ((f = fopen(p, "r")) == NULL) { ret = ENOENT; hx509_set_error_string(context, 0, ret, "Failed to open PEM file \"%s\": %s", p, strerror(errno)); goto out; } - rk_cloexec_file(f2); + rk_cloexec_file(f); - ret = hx509_pem_read(context, f2, pem_func, &pem_ctx); - fclose(f2); + ret = hx509_pem_read(context, f, pem_func, &pem_ctx); + fclose(f); if (ret != 0 && ret != HX509_PARSING_KEY_FAILED) goto out; else if (ret == HX509_PARSING_KEY_FAILED) { @@ -461,7 +461,7 @@ file_init_common(hx509_context context, } } - ret = _hx509_collector_collect_certs(context, pem_ctx.c, &f->certs); + ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs); if (ret) goto out; @@ -470,17 +470,17 @@ file_init_common(hx509_context context, int i; for (i = 0; keys[i]; i++) - _hx509_certs_keys_add(context, f->certs, keys[i]); + _hx509_certs_keys_add(context, ksf->certs, keys[i]); _hx509_certs_keys_free(context, keys); } out: if (ret == 0) - *data = f; + *data = ksf; else { - if (f->fn) - free(f->fn); - free(f); + if (ksf->fn) + free(ksf->fn); + free(ksf); } if (pem_ctx.c) _hx509_collector_free(pem_ctx.c); @@ -507,10 +507,10 @@ file_init_der(hx509_context context, static int file_free(hx509_certs certs, void *data) { - struct ks_file *f = data; - hx509_certs_free(&f->certs); - free(f->fn); - free(f); + struct ks_file *ksf = data; + hx509_certs_free(&ksf->certs); + free(ksf->fn); + free(ksf); return 0; } @@ -558,20 +558,20 @@ static int file_store(hx509_context context, hx509_certs certs, void *data, int flags, hx509_lock lock) { - struct ks_file *f = data; + struct ks_file *ksf = data; struct store_ctx sc; int ret; - sc.f = fopen(f->fn, "w"); + sc.f = fopen(ksf->fn, "w"); if (sc.f == NULL) { hx509_set_error_string(context, 0, ENOENT, "Failed to open file %s for writing"); return ENOENT; } rk_cloexec_file(sc.f); - sc.format = f->format; + sc.format = ksf->format; - ret = hx509_certs_iter(context, f->certs, store_func, &sc); + ret = hx509_certs_iter(context, ksf->certs, store_func, &sc); fclose(sc.f); return ret; } @@ -579,24 +579,24 @@ file_store(hx509_context context, static int file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) { - struct ks_file *f = data; - return hx509_certs_add(context, f->certs, c); + struct ks_file *ksf = data; + return hx509_certs_add(context, ksf->certs, c); } static int file_iter_start(hx509_context context, hx509_certs certs, void *data, void **cursor) { - struct ks_file *f = data; - return hx509_certs_start_seq(context, f->certs, cursor); + struct ks_file *ksf = data; + return hx509_certs_start_seq(context, ksf->certs, cursor); } static int file_iter(hx509_context context, hx509_certs certs, void *data, void *iter, hx509_cert *cert) { - struct ks_file *f = data; - return hx509_certs_next_cert(context, f->certs, iter, cert); + struct ks_file *ksf = data; + return hx509_certs_next_cert(context, ksf->certs, iter, cert); } static int @@ -605,8 +605,8 @@ file_iter_end(hx509_context context, void *data, void *cursor) { - struct ks_file *f = data; - return hx509_certs_end_seq(context, f->certs, cursor); + struct ks_file *ksf = data; + return hx509_certs_end_seq(context, ksf->certs, cursor); } static int @@ -615,8 +615,8 @@ file_getkeys(hx509_context context, void *data, hx509_private_key **keys) { - struct ks_file *f = data; - return _hx509_certs_keys_get(context, f->certs, keys); + struct ks_file *ksf = data; + return _hx509_certs_keys_get(context, ksf->certs, keys); } static int @@ -625,8 +625,8 @@ file_addkey(hx509_context context, void *data, hx509_private_key key) { - struct ks_file *f = data; - return _hx509_certs_keys_add(context, f->certs, key); + struct ks_file *ksf = data; + return _hx509_certs_keys_add(context, ksf->certs, key); } static struct hx509_keyset_ops keyset_file = { diff --git a/source4/heimdal/lib/hx509/lock.c b/source4/heimdal/lib/hx509/lock.c index 219a301928..07e9d36125 100644 --- a/source4/heimdal/lib/hx509/lock.c +++ b/source4/heimdal/lib/hx509/lock.c @@ -214,10 +214,12 @@ hx509_lock_prompt(hx509_lock lock, hx509_prompt *prompt) void hx509_lock_free(hx509_lock lock) { - hx509_certs_free(&lock->certs); - hx509_lock_reset_passwords(lock); - memset(lock, 0, sizeof(*lock)); - free(lock); + if (lock) { + hx509_certs_free(&lock->certs); + hx509_lock_reset_passwords(lock); + memset(lock, 0, sizeof(*lock)); + free(lock); + } } int diff --git a/source4/heimdal/lib/hx509/name.c b/source4/heimdal/lib/hx509/name.c index c5844f98cc..b544ecb7ff 100644 --- a/source4/heimdal/lib/hx509/name.c +++ b/source4/heimdal/lib/hx509/name.c @@ -243,11 +243,7 @@ _hx509_Name_to_string(const Name *n, char **str) break; } case choice_DirectoryString_teletexString: - ss = malloc(ds->u.teletexString.length + 1); - if (ss == NULL) - _hx509_abort("allocation failure"); /* XXX */ - memcpy(ss, ds->u.teletexString.data, ds->u.teletexString.length); - ss[ds->u.teletexString.length] = '\0'; + ss = ds->u.teletexString; break; case choice_DirectoryString_universalString: { const uint32_t *uni = ds->u.universalString.data; @@ -279,8 +275,7 @@ _hx509_Name_to_string(const Name *n, char **str) len = strlen(ss); append_string(str, &total_len, ss, len, 1); if (ds->element == choice_DirectoryString_universalString || - ds->element == choice_DirectoryString_bmpString || - ds->element == choice_DirectoryString_teletexString) + ds->element == choice_DirectoryString_bmpString) { free(ss); } @@ -341,7 +336,7 @@ dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen) COPYCHARARRAY(ds, printableString, len, name); break; case choice_DirectoryString_teletexString: - COPYVOIDARRAY(ds, teletexString, len, name); + COPYCHARARRAY(ds, teletexString, len, name); break; case choice_DirectoryString_bmpString: COPYVALARRAY(ds, bmpString, len, name); @@ -930,12 +925,12 @@ hx509_general_name_unparse(GeneralName *name, char **str) switch (name->element) { case choice_GeneralName_otherName: { - char *str2; - hx509_oid_sprint(&name->u.otherName.type_id, &str2); - if (str2 == NULL) + char *oid; + hx509_oid_sprint(&name->u.otherName.type_id, &oid); + if (oid == NULL) return ENOMEM; - strpool = rk_strpoolprintf(strpool, "otherName: %s", str2); - free(str2); + strpool = rk_strpoolprintf(strpool, "otherName: %s", oid); + free(oid); break; } case choice_GeneralName_rfc822Name: @@ -990,12 +985,12 @@ hx509_general_name_unparse(GeneralName *name, char **str) break; } case choice_GeneralName_registeredID: { - char *str2; - hx509_oid_sprint(&name->u.registeredID, &str2); - if (str2 == NULL) + char *oid; + hx509_oid_sprint(&name->u.registeredID, &oid); + if (oid == NULL) return ENOMEM; - strpool = rk_strpoolprintf(strpool, "registeredID: %s", str2); - free(str2); + strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid); + free(oid); break; } default: diff --git a/source4/heimdal/lib/hx509/revoke.c b/source4/heimdal/lib/hx509/revoke.c index 74f2d74679..21140b3c7e 100644 --- a/source4/heimdal/lib/hx509/revoke.c +++ b/source4/heimdal/lib/hx509/revoke.c @@ -1004,17 +1004,17 @@ hx509_ocsp_request(hx509_context context, es = req.tbsRequest.requestExtensions; - es->val = calloc(es->len, sizeof(es->val[0])); + es->val = calloc(1, sizeof(es->val[0])); if (es->val == NULL) { ret = ENOMEM; goto out; } - es->len = 1; ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID); if (ret) { free_OCSPRequest(&req); return ret; } + es->len = 1; es->val[0].extnValue.data = malloc(10); if (es->val[0].extnValue.data == NULL) { diff --git a/source4/heimdal/lib/hx509/sel.c b/source4/heimdal/lib/hx509/sel.c index c5e760569a..5932ce84c3 100644 --- a/source4/heimdal/lib/hx509/sel.c +++ b/source4/heimdal/lib/hx509/sel.c @@ -176,7 +176,6 @@ _hx509_expr_eval(hx509_context context, hx509_env env, struct hx_expr *expr) default: _hx509_abort("hx509 eval expr with unknown op: %d", (int)expr->op); } - return 0; } void diff --git a/source4/heimdal/lib/krb5/auth_context.c b/source4/heimdal/lib/krb5/auth_context.c index bfc183d168..dfb9f6a0e3 100644 --- a/source4/heimdal/lib/krb5/auth_context.c +++ b/source4/heimdal/lib/krb5/auth_context.c @@ -171,10 +171,10 @@ krb5_auth_con_genaddrs(krb5_context context, if (auth_context->local_address == NULL) { len = sizeof(ss_local); if(getsockname(fd, local, &len) < 0) { + char buf[128]; ret = errno; - krb5_set_error_message(context, ret, - "getsockname: %s", - strerror(ret)); + strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, "getsockname: %s", buf); goto out; } ret = krb5_sockaddr2address (context, local, &local_k_address); @@ -189,9 +189,10 @@ krb5_auth_con_genaddrs(krb5_context context, if(flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR) { len = sizeof(ss_remote); if(getpeername(fd, remote, &len) < 0) { + char buf[128]; ret = errno; - krb5_set_error_message(context, ret, - "getpeername: %s", strerror(ret)); + strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, "getpeername: %s", buf); goto out; } ret = krb5_sockaddr2address (context, remote, &remote_k_address); diff --git a/source4/heimdal/lib/krb5/build_auth.c b/source4/heimdal/lib/krb5/build_auth.c index bf77fd4e77..a845e0ac33 100644 --- a/source4/heimdal/lib/krb5/build_auth.c +++ b/source4/heimdal/lib/krb5/build_auth.c @@ -100,35 +100,30 @@ make_etypelist(krb5_context context, } krb5_error_code KRB5_LIB_FUNCTION -krb5_build_authenticator (krb5_context context, +_krb5_build_authenticator(krb5_context context, krb5_auth_context auth_context, krb5_enctype enctype, krb5_creds *cred, Checksum *cksum, - Authenticator **auth_result, krb5_data *result, krb5_key_usage usage) { - Authenticator *auth; + Authenticator auth; u_char *buf = NULL; size_t buf_size; size_t len; krb5_error_code ret; krb5_crypto crypto; - auth = calloc(1, sizeof(*auth)); - if (auth == NULL) { - krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); - return ENOMEM; - } + memset(&auth, 0, sizeof(auth)); - auth->authenticator_vno = 5; - copy_Realm(&cred->client->realm, &auth->crealm); - copy_PrincipalName(&cred->client->name, &auth->cname); + auth.authenticator_vno = 5; + copy_Realm(&cred->client->realm, &auth.crealm); + copy_PrincipalName(&cred->client->name, &auth.cname); - krb5_us_timeofday (context, &auth->ctime, &auth->cusec); + krb5_us_timeofday (context, &auth.ctime, &auth.cusec); - ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth->subkey); + ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth.subkey); if(ret) goto fail; @@ -137,33 +132,43 @@ krb5_build_authenticator (krb5_context context, krb5_generate_seq_number (context, &cred->session, &auth_context->local_seqnumber); - ALLOC(auth->seq_number, 1); - if(auth->seq_number == NULL) { + ALLOC(auth.seq_number, 1); + if(auth.seq_number == NULL) { ret = ENOMEM; goto fail; } - *auth->seq_number = auth_context->local_seqnumber; + *auth.seq_number = auth_context->local_seqnumber; } else - auth->seq_number = NULL; - auth->authorization_data = NULL; - auth->cksum = cksum; - - if (cksum != NULL && cksum->cksumtype == CKSUMTYPE_GSSAPI) { - /* - * This is not GSS-API specific, we only enable it for - * GSS for now - */ - ret = make_etypelist(context, &auth->authorization_data); + auth.seq_number = NULL; + auth.authorization_data = NULL; + + if (cksum) { + ALLOC(auth.cksum, 1); + if (auth.cksum == NULL) { + ret = ENOMEM; + goto fail; + } + ret = copy_Checksum(cksum, auth.cksum); if (ret) goto fail; + + if (auth.cksum->cksumtype == CKSUMTYPE_GSSAPI) { + /* + * This is not GSS-API specific, we only enable it for + * GSS for now + */ + ret = make_etypelist(context, &auth.authorization_data); + if (ret) + goto fail; + } } /* XXX - Copy more to auth_context? */ - auth_context->authenticator->ctime = auth->ctime; - auth_context->authenticator->cusec = auth->cusec; + auth_context->authenticator->ctime = auth.ctime; + auth_context->authenticator->cusec = auth.cusec; - ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, auth, &len, ret); + ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, &auth, &len, ret); if (ret) goto fail; if(buf_size != len) @@ -175,7 +180,7 @@ krb5_build_authenticator (krb5_context context, ret = krb5_encrypt (context, crypto, usage /* KRB5_KU_AP_REQ_AUTH */, - buf + buf_size - len, + buf, len, result); krb5_crypto_destroy(context, crypto); @@ -183,20 +188,9 @@ krb5_build_authenticator (krb5_context context, if (ret) goto fail; + fail: + free_Authenticator (&auth); free (buf); - if (auth_result) - *auth_result = auth; - else { - /* Don't free the `cksum', it's allocated by the caller */ - auth->cksum = NULL; - free_Authenticator (auth); - free (auth); - } - return ret; - fail: - free_Authenticator (auth); - free (auth); - free (buf); return ret; } diff --git a/source4/heimdal/lib/krb5/context.c b/source4/heimdal/lib/krb5/context.c index 8bf8b79022..79e1000fd0 100644 --- a/source4/heimdal/lib/krb5/context.c +++ b/source4/heimdal/lib/krb5/context.c @@ -304,6 +304,12 @@ krb5_init_context(krb5_context *context) cc_ops_register(p); kt_ops_register(p); +#ifdef PKINIT + ret = hx509_context_init(&p->hx509ctx); + if (ret) + goto out; +#endif + out: if(ret) { krb5_free_context(p); @@ -816,31 +822,6 @@ krb5_get_default_in_tkt_etypes(krb5_context context, } /** - * Return the error string for the error code. The caller must not - * free the string. - * - * @param context Kerberos 5 context. - * @param code Kerberos error code. - * - * @return the error message matching code - * - * @ingroup krb5 - */ - -const char* KRB5_LIB_FUNCTION -krb5_get_err_text(krb5_context context, krb5_error_code code) -{ - const char *p = NULL; - if(context != NULL) - p = com_right(context->et_list, code); - if(p == NULL) - p = strerror(code); - if (p == NULL) - p = "Unknown error"; - return p; -} - -/** * Init the built-in ets in the Kerberos library. * * @param context kerberos context to add the ets too diff --git a/source4/heimdal/lib/krb5/crypto.c b/source4/heimdal/lib/krb5/crypto.c index bdcdb2ea0a..68233c290d 100644 --- a/source4/heimdal/lib/krb5/crypto.c +++ b/source4/heimdal/lib/krb5/crypto.c @@ -2058,7 +2058,7 @@ evp_encrypt(krb5_context context, return 0; } -static const char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 }; +static const unsigned char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 }; static krb5_error_code evp_encrypt_cts(krb5_context context, diff --git a/source4/heimdal/lib/krb5/error_string.c b/source4/heimdal/lib/krb5/error_string.c index 829c080a55..d2661dcaf5 100644 --- a/source4/heimdal/lib/krb5/error_string.c +++ b/source4/heimdal/lib/krb5/error_string.c @@ -104,6 +104,68 @@ krb5_vset_error_message (krb5_context context, krb5_error_code ret, HEIMDAL_MUTEX_unlock(context->mutex); } +/** + * Prepend the context full error string for a specific error code. + * The error that is stored should be internationalized. + * + * @param context Kerberos 5 context + * @param ret The error code + * @param fmt Error string for the error code + * @param ... printf(3) style parameters. + * + * @ingroup krb5_error + */ + +void KRB5_LIB_FUNCTION +krb5_prepend_error_message(krb5_context context, krb5_error_code ret, + const char *fmt, ...) + __attribute__ ((format (printf, 3, 4))) +{ + va_list ap; + + va_start(ap, fmt); + krb5_vset_error_message (context, ret, fmt, ap); + va_end(ap); +} + +/** + * Prepend the contexts's full error string for a specific error code. + * + * @param context Kerberos 5 context + * @param ret The error code + * @param fmt Error string for the error code + * @param args printf(3) style parameters. + * + * @ingroup krb5_error + */ + +void KRB5_LIB_FUNCTION +krb5_vprepend_error_message (krb5_context context, krb5_error_code ret, + const char *fmt, va_list args) + __attribute__ ((format (printf, 3, 0))) +{ + char *str, *str2; + HEIMDAL_MUTEX_lock(context->mutex); + if (context->error_code != ret) { + HEIMDAL_MUTEX_unlock(context->mutex); + return; + } + vasprintf(&str, fmt, args); + if (context->error_string) { + int e; + + e = asprintf(&str2, "%s: %s", str, context->error_string); + free(context->error_string); + if (e < 0) + context->error_string = NULL; + else + context->error_string = str2; + free(str); + } else + context->error_string = str; + HEIMDAL_MUTEX_unlock(context->mutex); +} + /** * Return the error message in context. On error or no error string, @@ -155,7 +217,6 @@ krb5_have_error_string(krb5_context context) const char * KRB5_LIB_FUNCTION krb5_get_error_message(krb5_context context, krb5_error_code code) { - const char *cstr; char *str; HEIMDAL_MUTEX_lock(context->mutex); @@ -172,10 +233,13 @@ krb5_get_error_message(krb5_context context, krb5_error_code code) if (code == 0) return strdup("Success"); - - cstr = krb5_get_err_text(context, code); - if (cstr) - return strdup(cstr); + { + const char *msg; + char buf[128]; + msg = com_right_r(context->et_list, code, buf, sizeof(buf)); + if (msg) + return strdup(msg); + } if (asprintf(&str, "<unknown error: %d>", (int)code) == -1) return NULL; @@ -199,3 +263,31 @@ krb5_free_error_message(krb5_context context, const char *msg) { free(rk_UNCONST(msg)); } + + +/** + * Return the error string for the error code. The caller must not + * free the string. + * + * This function is deprecated since its not threadsafe. + * + * @param context Kerberos 5 context. + * @param code Kerberos error code. + * + * @return the error message matching code + * + * @ingroup krb5 + */ + +const char* KRB5_LIB_FUNCTION +krb5_get_err_text(krb5_context context, krb5_error_code code) KRB5_DEPRECATED +{ + const char *p = NULL; + if(context != NULL) + p = com_right(context->et_list, code); + if(p == NULL) + p = strerror(code); + if (p == NULL) + p = "Unknown error"; + return p; +} diff --git a/source4/heimdal/lib/krb5/fcache.c b/source4/heimdal/lib/krb5/fcache.c index f8e74f1ddc..cda15e483b 100644 --- a/source4/heimdal/lib/krb5/fcache.c +++ b/source4/heimdal/lib/krb5/fcache.c @@ -95,13 +95,15 @@ _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive, N_("timed out locking cache file %s", "file"), filename); break; - default: + default: { + char buf[128]; + strerror_r(ret, buf, sizeof(buf)); krb5_set_error_message(context, ret, N_("error locking cache file %s: %s", - "file, error"), - filename, strerror(ret)); + "file, error"), filename, buf); break; } + } return ret; } @@ -127,12 +129,14 @@ _krb5_xunlock(krb5_context context, int fd) case EINVAL: /* filesystem doesn't support locking, let the user have it */ ret = 0; break; - default: + default: { + char buf[128]; + strerror_r(ret, buf, sizeof(buf)); krb5_set_error_message(context, ret, - N_("Failed to unlock file: %s", ""), - strerror(ret)); + N_("Failed to unlock file: %s", ""), buf); break; } + } return ret; } @@ -369,9 +373,11 @@ fcc_open(krb5_context context, int fd; fd = open(filename, flags, mode); if(fd < 0) { + char buf[128]; ret = errno; + strerror_r(ret, buf, sizeof(buf)); krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"), - filename, strerror(ret)); + filename, buf); return ret; } rk_cloexec(fd); @@ -431,9 +437,11 @@ fcc_initialize(krb5_context context, fcc_unlock(context, fd); if (close(fd) < 0) if (ret == 0) { + char buf[128]; ret = errno; + strerror_r(ret, buf, sizeof(buf)); krb5_set_error_message (context, ret, N_("close %s: %s", ""), - FILENAME(id), strerror(ret)); + FILENAME(id), buf); } return ret; } @@ -485,9 +493,11 @@ fcc_store_cred(krb5_context context, fcc_unlock(context, fd); if (close(fd) < 0) { if (ret == 0) { + char buf[128]; + strerror_r(ret, buf, sizeof(buf)); ret = errno; krb5_set_error_message (context, ret, N_("close %s: %s", ""), - FILENAME(id), strerror(ret)); + FILENAME(id), buf); } } return ret; @@ -875,12 +885,13 @@ fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) ret = rename(FILENAME(from), FILENAME(to)); if (ret && errno != EXDEV) { + char buf[128]; ret = errno; + strerror_r(ret, buf, sizeof(buf)); krb5_set_error_message(context, ret, N_("Rename of file from %s " "to %s failed: %s", ""), - FILENAME(from), FILENAME(to), - strerror(ret)); + FILENAME(from), FILENAME(to), buf); return ret; } else if (ret && errno == EXDEV) { /* make a copy and delete the orignal */ diff --git a/source4/heimdal/lib/krb5/generate_seq_number.c b/source4/heimdal/lib/krb5/generate_seq_number.c index 2764f1a914..b7bd8b99f8 100644 --- a/source4/heimdal/lib/krb5/generate_seq_number.c +++ b/source4/heimdal/lib/krb5/generate_seq_number.c @@ -38,23 +38,11 @@ krb5_generate_seq_number(krb5_context context, const krb5_keyblock *key, uint32_t *seqno) { - krb5_error_code ret; - krb5_keyblock *subkey; - uint32_t q; - u_char *p; - int i; - - ret = krb5_generate_subkey (context, key, &subkey); - if (ret) - return ret; - - q = 0; - for (p = (u_char *)subkey->keyvalue.data, i = 0; - i < subkey->keyvalue.length; - ++i, ++p) - q = (q << 8) | *p; - q &= 0xffffffff; - *seqno = q; - krb5_free_keyblock (context, subkey); + if (RAND_bytes((void *)seqno, sizeof(*seqno)) != 1) + krb5_abortx(context, "Failed to generate random block"); + /* MIT used signed numbers, lets not stomp into that space directly */ + *seqno &= 0x3fffffff; + if (*seqno == 0) + *seqno = 1; return 0; } diff --git a/source4/heimdal/lib/krb5/generate_subkey.c b/source4/heimdal/lib/krb5/generate_subkey.c index efb6cce288..003a66ac01 100644 --- a/source4/heimdal/lib/krb5/generate_subkey.c +++ b/source4/heimdal/lib/krb5/generate_subkey.c @@ -33,13 +33,18 @@ #include <krb5_locl.h> -krb5_error_code KRB5_LIB_FUNCTION -krb5_generate_subkey(krb5_context context, - const krb5_keyblock *key, - krb5_keyblock **subkey) -{ - return krb5_generate_subkey_extended(context, key, key->keytype, subkey); -} +/** + * Generate subkey, from keyblock + * + * @param context kerberos context + * @param key session key + * @param etype encryption type of subkey, if ETYPE_NULL, use key's enctype + * @param subkey returned new, free with krb5_free_keyblock(). + * + * @return 0 on success or a Kerberos 5 error code + * +* @ingroup krb5_crypto + */ krb5_error_code KRB5_LIB_FUNCTION krb5_generate_subkey_extended(krb5_context context, diff --git a/source4/heimdal/lib/krb5/get_cred.c b/source4/heimdal/lib/krb5/get_cred.c index 10417f1a52..63152bbfa6 100644 --- a/source4/heimdal/lib/krb5/get_cred.c +++ b/source4/heimdal/lib/krb5/get_cred.c @@ -32,6 +32,7 @@ */ #include <krb5_locl.h> +#include <assert.h> /* * Take the `body' and encode it into `padata' using the credentials @@ -79,7 +80,7 @@ static krb5_error_code set_auth_data (krb5_context context, KDC_REQ_BODY *req_body, krb5_authdata *authdata, - krb5_keyblock *key) + krb5_keyblock *subkey) { if(authdata->len) { size_t len, buf_size; @@ -101,7 +102,7 @@ set_auth_data (krb5_context context, N_("malloc: out of memory", "")); return ENOMEM; } - ret = krb5_crypto_init(context, key, 0, &crypto); + ret = krb5_crypto_init(context, subkey, 0, &crypto); if (ret) { free (buf); free (req_body->enc_authorization_data); @@ -111,7 +112,6 @@ set_auth_data (krb5_context context, krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, - /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */ buf, len, 0, @@ -143,7 +143,9 @@ init_tgs_req (krb5_context context, krb5_keyblock **subkey, TGS_REQ *t) { + krb5_auth_context ac = NULL; krb5_error_code ret = 0; + krb5_keyblock *key = NULL; memset(t, 0, sizeof(*t)); t->pvno = 5; @@ -238,60 +240,39 @@ init_tgs_req (krb5_context context, } } - { - krb5_auth_context ac; - krb5_keyblock *key = NULL; - - ret = krb5_auth_con_init(context, &ac); - if(ret) - goto fail; - - if (krb5_config_get_bool_default(context, NULL, FALSE, - "realms", - krbtgt->server->realm, - "tgs_require_subkey", - NULL)) - { - ret = krb5_generate_subkey (context, &krbtgt->session, &key); - if (ret) { - krb5_auth_con_free (context, ac); - goto fail; - } - - ret = krb5_auth_con_setlocalsubkey(context, ac, key); - if (ret) { - if (key) - krb5_free_keyblock (context, key); - krb5_auth_con_free (context, ac); - goto fail; - } - } - - ret = set_auth_data (context, &t->req_body, &in_creds->authdata, - key ? key : &krbtgt->session); - if (ret) { - if (key) - krb5_free_keyblock (context, key); - krb5_auth_con_free (context, ac); - goto fail; - } + ret = krb5_auth_con_init(context, &ac); + if(ret) + goto fail; + + ret = krb5_generate_subkey_extended(context, &krbtgt->session, + ETYPE_NULL, &key); + if (ret) + goto fail; + + ret = krb5_auth_con_setlocalsubkey(context, ac, key); + if (ret) + goto fail; + + ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key); + if (ret) + goto fail; + + ret = make_pa_tgs_req(context, + ac, + &t->req_body, + &t->padata->val[0], + krbtgt); + if(ret) + goto fail; - ret = make_pa_tgs_req(context, - ac, - &t->req_body, - &t->padata->val[0], - krbtgt); - if(ret) { - if (key) - krb5_free_keyblock (context, key); - krb5_auth_con_free(context, ac); - goto fail; - } - *subkey = key; - - krb5_auth_con_free(context, ac); - } + *subkey = key; + key = NULL; + fail: + if (key) + krb5_free_keyblock (context, key); + if (ac) + krb5_auth_con_free(context, ac); if (ret) { t->req_body.addresses = NULL; free_TGS_REQ (t); @@ -349,17 +330,12 @@ decrypt_tkt_with_subkey (krb5_context context, size_t size; krb5_crypto crypto; - ret = krb5_crypto_init(context, key, 0, &crypto); - if (ret) - return ret; - ret = krb5_decrypt_EncryptedData (context, - crypto, - usage, - &dec_rep->kdc_rep.enc_part, - &data); - krb5_crypto_destroy(context, crypto); - if(ret && subkey){ - /* DCE compat -- try to decrypt with subkey */ + assert(usage == 0); + + /* + * start out with trying with subkey if we have one + */ + if (subkey) { ret = krb5_crypto_init(context, subkey, 0, &crypto); if (ret) return ret; @@ -370,6 +346,17 @@ decrypt_tkt_with_subkey (krb5_context context, &data); krb5_crypto_destroy(context, crypto); } + if (subkey == NULL || ret) { + ret = krb5_crypto_init(context, key, 0, &crypto); + if (ret) + return ret; + ret = krb5_decrypt_EncryptedData (context, + crypto, + KRB5_KU_TGS_REP_ENC_PART_SESSION, + &dec_rep->kdc_rep.enc_part, + &data); + krb5_crypto_destroy(context, crypto); + } if (ret) return ret; @@ -549,7 +536,7 @@ get_cred_kdc(krb5_context context, out_creds, &krbtgt->session, NULL, - KRB5_KU_TGS_REP_ENC_PART_SESSION, + 0, &krbtgt->addresses, nonce, eflags, @@ -574,10 +561,8 @@ out: free_METHOD_DATA(&padata); krb5_data_free(&resp); krb5_data_free(&enc); - if(subkey){ - krb5_free_keyblock_contents(context, subkey); - free(subkey); - } + if(subkey) + krb5_free_keyblock(context, subkey); return ret; } @@ -898,6 +883,12 @@ get_cred_kdc_referral(krb5_context context, int loop = 0; int ok_as_delegate = 1; + if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) { + krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, + N_("Name too short to do referals, skipping", "")); + return KRB5KDC_ERR_PATH_NOT_ACCEPTED; + } + memset(&tgt, 0, sizeof(tgt)); memset(&ticket, 0, sizeof(ticket)); @@ -1087,6 +1078,12 @@ krb5_get_credentials_with_flags(krb5_context context, krb5_creds *res_creds; int i; + if (in_creds->session.keytype) { + ret = krb5_enctype_valid(context, in_creds->session.keytype); + if (ret) + return ret; + } + *out_creds = NULL; res_creds = calloc(1, sizeof(*res_creds)); if (res_creds == NULL) { @@ -1282,6 +1279,12 @@ krb5_get_creds(krb5_context context, krb5_creds *res_creds; int i; + if (opt && opt->enctype) { + ret = krb5_enctype_valid(context, opt->enctype); + if (ret) + return ret; + } + memset(&in_creds, 0, sizeof(in_creds)); in_creds.server = rk_UNCONST(inprinc); @@ -1289,7 +1292,10 @@ krb5_get_creds(krb5_context context, if (ret) return ret; - options = opt->options; + if (opt) + options = opt->options; + else + options = 0; flags.i = 0; *out_creds = NULL; @@ -1301,7 +1307,7 @@ krb5_get_creds(krb5_context context, return ENOMEM; } - if (opt->enctype) { + if (opt && opt->enctype) { in_creds.session.keytype = opt->enctype; options |= KRB5_TC_MATCH_KEYTYPE; } @@ -1312,7 +1318,7 @@ krb5_get_creds(krb5_context context, */ ret = krb5_cc_retrieve_cred(context, ccache, - opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0, + options & KRB5_TC_MATCH_KEYTYPE, &in_creds, res_creds); /* * If we got a credential, check if credential is expired before diff --git a/source4/heimdal/lib/krb5/get_for_creds.c b/source4/heimdal/lib/krb5/get_for_creds.c index 19e48173df..8c58dae187 100644 --- a/source4/heimdal/lib/krb5/get_for_creds.c +++ b/source4/heimdal/lib/krb5/get_for_creds.c @@ -137,13 +137,12 @@ krb5_fwd_tgt_creds (krb5_context context, memset (&creds, 0, sizeof(creds)); creds.client = client; - ret = krb5_build_principal(context, - &creds.server, - strlen(client_realm), - client_realm, - KRB5_TGS_NAME, - client_realm, - NULL); + ret = krb5_make_principal(context, + &creds.server, + client_realm, + KRB5_TGS_NAME, + client_realm, + NULL); if (ret) return ret; diff --git a/source4/heimdal/lib/krb5/krb5_locl.h b/source4/heimdal/lib/krb5/krb5_locl.h index 71dc1327c6..d436215769 100644 --- a/source4/heimdal/lib/krb5/krb5_locl.h +++ b/source4/heimdal/lib/krb5/krb5_locl.h @@ -136,6 +136,8 @@ struct sockaddr_dl; #include <door.h> #endif +#include <com_err.h> + #include <roken.h> #include <parse_time.h> #include <base64.h> @@ -151,6 +153,7 @@ struct sockaddr_dl; struct send_to_kdc; /* XXX glue for pkinit */ +struct hx509_certs_data; struct krb5_pk_identity; struct krb5_pk_cert; struct ContentInfo; @@ -265,6 +268,9 @@ typedef struct krb5_context_data { #define KRB5_CTX_F_CHECK_PAC 2 #define KRB5_CTX_F_HOMEDIR_ACCESS 4 struct send_to_kdc *send_to_kdc; +#ifdef PKINIT + hx509_context hx509ctx; +#endif } krb5_context_data; #define KRB5_DEFAULT_CCNAME_FILE "FILE:/tmp/krb5cc_%{uid}" @@ -295,7 +301,6 @@ typedef struct krb5_context_data { #ifdef PKINIT struct krb5_pk_identity { - hx509_context hx509ctx; hx509_verify_ctx verify_ctx; hx509_certs certs; hx509_cert cert; diff --git a/source4/heimdal/lib/krb5/mk_error.c b/source4/heimdal/lib/krb5/mk_error.c index f623fc495b..0de30e4ddb 100644 --- a/source4/heimdal/lib/krb5/mk_error.c +++ b/source4/heimdal/lib/krb5/mk_error.c @@ -44,6 +44,7 @@ krb5_mk_error(krb5_context context, int *client_usec, krb5_data *reply) { + const char *e_text2 = NULL; KRB_ERROR msg; krb5_timestamp sec; int32_t usec; @@ -62,7 +63,7 @@ krb5_mk_error(krb5_context context, /* Make sure we only send `protocol' error codes */ if(error_code < KRB5KDC_ERR_NONE || error_code >= KRB5_ERR_RCSID) { if(e_text == NULL) - e_text = krb5_get_err_text(context, error_code); + e_text = e_text2 = krb5_get_error_message(context, error_code); error_code = KRB5KRB_ERR_GENERIC; } msg.error_code = error_code - KRB5KDC_ERR_NONE; @@ -82,6 +83,8 @@ krb5_mk_error(krb5_context context, } ASN1_MALLOC_ENCODE(KRB_ERROR, reply->data, reply->length, &msg, &len, ret); + if (e_text2) + krb5_free_error_message(context, e_text2); if (ret) return ret; if(reply->length != len) diff --git a/source4/heimdal/lib/krb5/mk_req_ext.c b/source4/heimdal/lib/krb5/mk_req_ext.c index d130272aa1..03fc93b02f 100644 --- a/source4/heimdal/lib/krb5/mk_req_ext.c +++ b/source4/heimdal/lib/krb5/mk_req_ext.c @@ -123,12 +123,11 @@ _krb5_mk_req_internal(krb5_context context, if (ret) goto out; - ret = krb5_build_authenticator (context, + ret = _krb5_build_authenticator(context, ac, ac->keyblock->keytype, in_creds, c_opt, - NULL, &authenticator, encrypt_usage); if (c_opt) diff --git a/source4/heimdal/lib/krb5/pkinit.c b/source4/heimdal/lib/krb5/pkinit.c index 2f6d7854a5..f6457aa5c9 100644 --- a/source4/heimdal/lib/krb5/pkinit.c +++ b/source4/heimdal/lib/krb5/pkinit.c @@ -74,6 +74,7 @@ struct krb5_pk_init_ctx_data { unsigned int require_krbtgt_otherName:1; unsigned int require_hostname_match:1; unsigned int trustedCertifiers:1; + unsigned int anonymous:1; }; static void @@ -193,15 +194,15 @@ find_cert(krb5_context context, struct krb5_pk_identity *id, for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) { ret = hx509_query_match_eku(q, cf[i].oid); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed setting %s OID", cf[i].type); return ret; } - ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert); + ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert); if (ret == 0) break; - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed finding certificate with %s OID", cf[i].type); } return ret; @@ -221,7 +222,7 @@ create_signature(krb5_context context, if (id->cert == NULL) flags |= HX509_CMS_SIGNATURE_NO_SIGNER; - ret = hx509_cms_create_signed_1(id->hx509ctx, + ret = hx509_cms_create_signed_1(context->hx509ctx, flags, eContentType, eContent->data, @@ -233,7 +234,7 @@ create_signature(krb5_context context, id->certs, sd_data); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Create CMS signedData"); return ret; } @@ -596,7 +597,7 @@ build_auth_pack(krb5_context context, if (a->supportedCMSTypes == NULL) return ENOMEM; - ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL, + ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, NULL, &a->supportedCMSTypes->val, &a->supportedCMSTypes->len); if (ret) @@ -756,7 +757,7 @@ pk_mk_padata(krb5_context context, free_PA_PK_AS_REQ(&req); goto out; } - ret = build_edi(context, ctx->id->hx509ctx, + ret = build_edi(context, context->hx509ctx, ctx->id->anchors, req.trustedCertifiers); if (ret) { krb5_set_error_message(context, ret, @@ -806,6 +807,12 @@ _krb5_pk_mk_padata(krb5_context context, krb5_pk_init_ctx ctx = c; int win2k_compat; + if (ctx->id->certs == NULL && ctx->anonymous == 0) { + krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY, + N_("PKINIT: No user certificate given", "")); + return HEIM_PKINIT_NO_PRIVATE_KEY; + } + win2k_compat = krb5_config_get_bool_default(context, NULL, FALSE, "realms", @@ -873,7 +880,7 @@ pk_verify_sign(krb5_context context, *signer = NULL; - ret = hx509_cms_verify_signed(id->hx509ctx, + ret = hx509_cms_verify_signed(context->hx509ctx, id->verify_ctx, HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH|HX509_CMS_VS_NO_KU_CHECK, data, @@ -884,7 +891,7 @@ pk_verify_sign(krb5_context context, content, &signer_certs); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "CMS verify signed failed"); return ret; } @@ -896,9 +903,9 @@ pk_verify_sign(krb5_context context, goto out; } - ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert); + ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed to get on of the signer certs"); goto out; } @@ -1040,7 +1047,7 @@ pk_verify_host(krb5_context context, krb5_error_code ret = 0; if (ctx->require_eku) { - ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert, + ret = hx509_cert_check_eku(context->hx509ctx, host->cert, &asn1_oid_id_pkkdcekuoid, 0); if (ret) { krb5_set_error_message(context, ret, @@ -1052,7 +1059,7 @@ pk_verify_host(krb5_context context, hx509_octet_string_list list; int i; - ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx, + ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx, host->cert, &asn1_oid_id_pkinit_san, &list); @@ -1102,7 +1109,7 @@ pk_verify_host(krb5_context context, return ret; if (hi) { - ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert, + ret = hx509_verify_hostname(context->hx509ctx, host->cert, ctx->require_hostname_match, HX509_HN_HOSTNAME, hi->hostname, @@ -1145,7 +1152,7 @@ pk_rd_pa_reply_enckey(krb5_context context, if (ctx->type == PKINIT_WIN2K) flags |= HX509_CMS_UE_ALLOW_WEAK; - ret = hx509_cms_unenvelope(ctx->id->hx509ctx, + ret = hx509_cms_unenvelope(context->hx509ctx, ctx->id->certs, flags, indata->data, @@ -1155,7 +1162,7 @@ pk_rd_pa_reply_enckey(krb5_context context, &contentType, &content); if (ret) { - pk_copy_error(context, ctx->id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed to unenvelope CMS data in PK-INIT reply"); return ret; } @@ -1167,8 +1174,26 @@ pk_rd_pa_reply_enckey(krb5_context context, heim_octet_string out; ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL); - if (ret) - goto out; + if (ret) { + /* windows LH with interesting CMS packets */ + size_t ph = 1 + der_length_len(content.length); + unsigned char *ptr = malloc(content.length + ph); + size_t l; + + memcpy(ptr + ph, content.data, content.length); + + ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length, + ASN1_C_UNIV, CONS, UT_Sequence, &l); + if (ret) + return ret; + free(content.data); + content.data = ptr; + content.length += ph; + + ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL); + if (ret) + goto out; + } if (der_heim_oid_cmp(&type, &asn1_oid_id_pkcs7_signedData)) { ret = EINVAL; /* XXX */ krb5_set_error_message(context, ret, @@ -1700,10 +1725,44 @@ hx_pass_prompter(void *data, const hx509_prompt *prompter) return 0; } +static krb5_error_code +_krb5_pk_set_user_id(krb5_context context, + krb5_pk_init_ctx ctx, + struct hx509_certs_data *certs) +{ + hx509_certs c = hx509_certs_ref(certs); + hx509_query *q = NULL; + int ret; + + if (ctx->id->certs) + hx509_certs_free(&ctx->id->certs); + if (ctx->id->cert) { + hx509_cert_free(ctx->id->cert); + ctx->id->cert = NULL; + } + + ctx->id->certs = c; + ctx->anonymous = 0; + + ret = hx509_query_alloc(context->hx509ctx, &q); + if (ret) { + pk_copy_error(context, context->hx509ctx, ret, + "Allocate query to find signing certificate"); + return ret; + } + + hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); + hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); + + ret = find_cert(context, ctx->id, q, &ctx->id->cert); + hx509_query_free(context->hx509ctx, q); + + return ret; +} + krb5_error_code KRB5_LIB_FUNCTION _krb5_pk_load_id(krb5_context context, struct krb5_pk_identity **ret_id, - int flags, const char *user_id, const char *anchor_id, char * const *chain_list, @@ -1713,7 +1772,6 @@ _krb5_pk_load_id(krb5_context context, char *password) { struct krb5_pk_identity *id = NULL; - hx509_lock lock = NULL; struct prompter p; int ret; @@ -1725,12 +1783,6 @@ _krb5_pk_load_id(krb5_context context, return HEIM_PKINIT_NO_VALID_CA; } - if (user_id == NULL && (flags & 4) == 0) { - krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY, - N_("PKINIT: No user certificate given", "")); - return HEIM_PKINIT_NO_PRIVATE_KEY; - } - /* load cert */ id = calloc(1, sizeof(*id)); @@ -1740,33 +1792,34 @@ _krb5_pk_load_id(krb5_context context, return ENOMEM; } - ret = hx509_context_init(&id->hx509ctx); - if (ret) - goto out; - - ret = hx509_lock_init(id->hx509ctx, &lock); - if (ret) { - pk_copy_error(context, id->hx509ctx, ret, "Failed init lock"); - goto out; - } - - if (password && password[0]) - hx509_lock_add_password(lock, password); - - if (prompter) { - p.context = context; - p.prompter = prompter; - p.prompter_data = prompter_data; + if (user_id) { + hx509_lock lock; - ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p); - if (ret) + ret = hx509_lock_init(context->hx509ctx, &lock); + if (ret) { + pk_copy_error(context, context->hx509ctx, ret, "Failed init lock"); goto out; - } + } + + if (password && password[0]) + hx509_lock_add_password(lock, password); + + if (prompter) { + p.context = context; + p.prompter = prompter; + p.prompter_data = prompter_data; + + ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p); + if (ret) { + hx509_lock_free(lock); + goto out; + } + } - if (user_id) { - ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs); + ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs); + hx509_lock_free(lock); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed to init cert certs"); goto out; } @@ -1774,26 +1827,26 @@ _krb5_pk_load_id(krb5_context context, id->certs = NULL; } - ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors); + ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed to init anchors"); goto out; } - ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain", + ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain", 0, NULL, &id->certpool); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed to init chain"); goto out; } while (chain_list && *chain_list) { - ret = hx509_certs_append(id->hx509ctx, id->certpool, + ret = hx509_certs_append(context->hx509ctx, id->certpool, NULL, *chain_list); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed to laod chain %s", *chain_list); goto out; @@ -1802,30 +1855,30 @@ _krb5_pk_load_id(krb5_context context, } if (revoke_list) { - ret = hx509_revoke_init(id->hx509ctx, &id->revokectx); + ret = hx509_revoke_init(context->hx509ctx, &id->revokectx); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed init revoke list"); goto out; } while (*revoke_list) { - ret = hx509_revoke_add_crl(id->hx509ctx, + ret = hx509_revoke_add_crl(context->hx509ctx, id->revokectx, *revoke_list); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed load revoke list"); goto out; } revoke_list++; } } else - hx509_context_set_missing_revoke(id->hx509ctx, 1); + hx509_context_set_missing_revoke(context->hx509ctx, 1); - ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx); + ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx); if (ret) { - pk_copy_error(context, id->hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed init verify context"); goto out; } @@ -1840,14 +1893,11 @@ _krb5_pk_load_id(krb5_context context, hx509_certs_free(&id->anchors); hx509_certs_free(&id->certpool); hx509_revoke_free(&id->revokectx); - hx509_context_free(&id->hx509ctx); + hx509_context_free(&context->hx509ctx); free(id); } else *ret_id = id; - if (lock) - hx509_lock_free(lock); - return ret; } @@ -2204,7 +2254,6 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt) hx509_cert_free(ctx->id->cert); hx509_certs_free(&ctx->id->anchors); hx509_certs_free(&ctx->id->certpool); - hx509_context_free(&ctx->id->hx509ctx); if (ctx->clientDHNonce) { krb5_free_data(NULL, ctx->clientDHNonce); @@ -2275,9 +2324,11 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context, x509_anchors = anchors; } + if (flags & 4) + opt->opt_private->pk_init_ctx->anonymous = 1; + ret = _krb5_pk_load_id(context, &opt->opt_private->pk_init_ctx->id, - flags, user_id, x509_anchors, pool, @@ -2292,31 +2343,14 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context, } if (opt->opt_private->pk_init_ctx->id->certs) { - hx509_query *q = NULL; - hx509_cert cert = NULL; - hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx; - - ret = hx509_query_alloc(hx509ctx, &q); - if (ret) { - pk_copy_error(context, hx509ctx, ret, - "Allocate query to find signing certificate"); - return ret; - } - - hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); - hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); - - ret = find_cert(context, opt->opt_private->pk_init_ctx->id, q, &cert); - hx509_query_free(hx509ctx, q); - if (ret) - return ret; - - opt->opt_private->pk_init_ctx->id->cert = cert; + _krb5_pk_set_user_id(context, + opt->opt_private->pk_init_ctx, + opt->opt_private->pk_init_ctx->id->certs); } else opt->opt_private->pk_init_ctx->id->cert = NULL; if ((flags & 2) == 0) { - hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx; + hx509_context hx509ctx = context->hx509ctx; hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert; opt->opt_private->pk_init_ctx->keyex = USE_DH; @@ -2353,6 +2387,33 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context, #endif } +krb5_error_code KRB5_LIB_FUNCTION +_krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context, + krb5_get_init_creds_opt *opt, + struct hx509_certs_data *certs) +{ +#ifdef PKINIT + if (opt->opt_private == NULL) { + krb5_set_error_message(context, EINVAL, + N_("PKINIT: on non extendable opt", "")); + return EINVAL; + } + if (opt->opt_private->pk_init_ctx == NULL) { + krb5_set_error_message(context, EINVAL, + N_("PKINIT: on pkinit context", "")); + return EINVAL; + } + + _krb5_pk_set_user_id(context, opt->opt_private->pk_init_ctx, certs); + + return 0; +#else + krb5_set_error_message(context, EINVAL, + N_("no support for PKINIT compiled in", "")); + return EINVAL; +#endif +} + #ifdef PKINIT static int @@ -2404,34 +2465,35 @@ krb5_error_code KRB5_LIB_FUNCTION _krb5_pk_enterprise_cert(krb5_context context, const char *user_id, krb5_const_realm realm, - krb5_principal *principal) + krb5_principal *principal, + struct hx509_certs_data **res) { #ifdef PKINIT krb5_error_code ret; - hx509_context hx509ctx; hx509_certs certs, result; hx509_cert cert; hx509_query *q; char *name; *principal = NULL; + if (res) + *res = NULL; - if (user_id == NULL) + if (user_id == NULL) { + krb5_clear_error_message(context); return ENOENT; + } - ret = hx509_context_init(&hx509ctx); - if (ret) - return ret; - - ret = hx509_certs_init(hx509ctx, user_id, 0, NULL, &certs); + ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs); if (ret) { - pk_copy_error(context, hx509ctx, ret, + pk_copy_error(context, context->hx509ctx, ret, "Failed to init cert certs"); return ret; } - ret = hx509_query_alloc(hx509ctx, &q); + ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { + krb5_set_error_message(context, ret, "out of memory"); hx509_certs_free(&certs); return ret; } @@ -2441,29 +2503,54 @@ _krb5_pk_enterprise_cert(krb5_context context, hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku); hx509_query_match_cmp_func(q, find_ms_san, NULL); - ret = hx509_certs_filter(hx509ctx, certs, q, &result); - hx509_query_free(hx509ctx, q); + ret = hx509_certs_filter(context->hx509ctx, certs, q, &result); + hx509_query_free(context->hx509ctx, q); hx509_certs_free(&certs); - if (ret) + if (ret) { + pk_copy_error(context, context->hx509ctx, ret, + "Failed to find PKINIT certificate"); return ret; + } - ret = hx509_get_one_cert(hx509ctx, result, &cert); + ret = hx509_get_one_cert(context->hx509ctx, result, &cert); hx509_certs_free(&result); - if (ret) - return ret; + if (ret) { + pk_copy_error(context, context->hx509ctx, ret, + "Failed to get one cert"); + goto out; + } - ret = get_ms_san(hx509ctx, cert, &name); - if (ret) - return ret; + ret = get_ms_san(context->hx509ctx, cert, &name); + if (ret) { + pk_copy_error(context, context->hx509ctx, ret, + "Failed to get MS SAN"); + goto out; + } ret = krb5_make_principal(context, principal, realm, name, NULL); free(name); - hx509_context_free(&hx509ctx); if (ret) - return ret; + goto out; krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL); - + + if (res) { + ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res); + if (ret) { + hx509_cert_free(cert); + goto out; + } + + ret = hx509_certs_add(context->hx509ctx, *res, cert); + if (ret) { + hx509_certs_free(res); + goto out; + } + } + + out: + hx509_cert_free(cert); + return ret; #else krb5_set_error_message(context, EINVAL, diff --git a/source4/heimdal/lib/krb5/principal.c b/source4/heimdal/lib/krb5/principal.c index 1483d59f9d..d854113a43 100644 --- a/source4/heimdal/lib/krb5/principal.c +++ b/source4/heimdal/lib/krb5/principal.c @@ -106,6 +106,17 @@ krb5_principal_set_type(krb5_context context, princ_type(principal) = type; } +/** + * Get the type of the principal + * + * @param context A Kerberos context. + * @param principal principal to get the type for + * + * @return the type of principal + * + * @ingroup krb5_principal + */ + int KRB5_LIB_FUNCTION krb5_principal_get_type(krb5_context context, krb5_const_principal principal) @@ -113,6 +124,17 @@ krb5_principal_get_type(krb5_context context, return princ_type(principal); } +/** + * Get the realm of the principal + * + * @param context A Kerberos context. + * @param principal principal to get the realm for + * + * @return realm of the principal, don't free or use after krb5_principal is freed + * + * @ingroup krb5_principal + */ + const char* KRB5_LIB_FUNCTION krb5_principal_get_realm(krb5_context context, krb5_const_principal principal) @@ -148,6 +170,19 @@ krb5_principal_get_num_comp(krb5_context context, return princ_num_comp(principal); } +/** + * Parse a name into a krb5_principal structure, flags controls the behavior. + * + * @param context Kerberos 5 context + * @param name name to parse into a Kerberos principal + * @param flags flags to control the behavior + * @param principal returned principal, free with krb5_free_principal(). + * + * @return An krb5 error code, see krb5_get_error_message(). + * + * @ingroup krb5_principal + */ + krb5_error_code KRB5_LIB_FUNCTION krb5_parse_name_flags(krb5_context context, const char *name, @@ -337,6 +372,18 @@ exit: return ret; } +/** + * Parse a name into a krb5_principal structure + * + * @param context Kerberos 5 context + * @param name name to parse into a Kerberos principal + * @param principal returned principal, free with krb5_free_principal(). + * + * @return An krb5 error code, see krb5_get_error_message(). + * + * @ingroup krb5_principal + */ + krb5_error_code KRB5_LIB_FUNCTION krb5_parse_name(krb5_context context, const char *name, @@ -630,6 +677,20 @@ krb5_principal_set_realm(krb5_context context, return 0; } +#ifndef HEIMDAL_SMALLER +/** + * Build a principal using vararg style building + * + * @param context A Kerberos context. + * @param principal returned principal + * @param rlen length of realm + * @param realm realm name + * @param ... a list of components ended with NULL. + * + * @return An krb5 error code, see krb5_get_error_message(). + * + * @ingroup krb5_principal + */ krb5_error_code KRB5_LIB_FUNCTION krb5_build_principal(krb5_context context, @@ -645,6 +706,43 @@ krb5_build_principal(krb5_context context, va_end(ap); return ret; } +#endif + +/** + * Build a principal using vararg style building + * + * @param context A Kerberos context. + * @param principal returned principal + * @param realm realm name + * @param ... a list of components ended with NULL. + * + * @return An krb5 error code, see krb5_get_error_message(). + * + * @ingroup krb5_principal + */ + +krb5_error_code KRB5_LIB_FUNCTION +krb5_make_principal(krb5_context context, + krb5_principal *principal, + krb5_const_realm realm, + ...) +{ + krb5_error_code ret; + krb5_realm r = NULL; + va_list ap; + if(realm == NULL) { + ret = krb5_get_default_realm(context, &r); + if(ret) + return ret; + realm = r; + } + va_start(ap, realm); + ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap); + va_end(ap); + if(r) + free(r); + return ret; +} static krb5_error_code append_component(krb5_context context, krb5_principal p, @@ -730,28 +828,6 @@ build_principal(krb5_context context, return 0; } -krb5_error_code KRB5_LIB_FUNCTION -krb5_make_principal(krb5_context context, - krb5_principal *principal, - krb5_const_realm realm, - ...) -{ - krb5_error_code ret; - krb5_realm r = NULL; - va_list ap; - if(realm == NULL) { - ret = krb5_get_default_realm(context, &r); - if(ret) - return ret; - realm = r; - } - va_start(ap, realm); - ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap); - va_end(ap); - if(r) - free(r); - return ret; -} krb5_error_code KRB5_LIB_FUNCTION krb5_build_principal_va(krb5_context context, @@ -789,6 +865,18 @@ krb5_build_principal_ext(krb5_context context, return ret; } +/** + * Copy a principal + * + * @param context A Kerberos context. + * @param inprinc principal to copy + * @param outprinc copied principal, free with krb5_free_principal() + * + * @return An krb5 error code, see krb5_get_error_message(). + * + * @ingroup krb5_principal + */ + krb5_error_code KRB5_LIB_FUNCTION krb5_copy_principal(krb5_context context, @@ -821,6 +909,8 @@ krb5_copy_principal(krb5_context context, * @return non zero if equal, 0 if not * * @ingroup krb5_principal + * @see krb5_principal_compare() + * @see krb5_realm_compare() */ krb5_boolean KRB5_LIB_FUNCTION @@ -854,6 +944,19 @@ _krb5_principal_compare_PrincipalName(krb5_context context, } +/** + * Compares the two principals, including realm of the principals and returns + * TRUE if they are the same and FALSE if not. + * + * @param context Kerberos 5 context + * @param princ1 first principal to compare + * @param princ2 second principal to compare + * + * @ingroup krb5_principal + * @see krb5_principal_compare_any_realm() + * @see krb5_realm_compare() + */ + /* * return TRUE iff princ1 == princ2 */ @@ -868,8 +971,16 @@ krb5_principal_compare(krb5_context context, return krb5_principal_compare_any_realm(context, princ1, princ2); } -/* +/** * return TRUE iff realm(princ1) == realm(princ2) + * + * @param context Kerberos 5 context + * @param princ1 first principal to compare + * @param princ2 second principal to compare + * + * @ingroup krb5_principal + * @see krb5_principal_compare_any_realm() + * @see krb5_principal_compare() */ krb5_boolean KRB5_LIB_FUNCTION @@ -880,8 +991,10 @@ krb5_realm_compare(krb5_context context, return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0; } -/* +/** * return TRUE iff princ matches pattern + * + * @ingroup krb5_principal */ krb5_boolean KRB5_LIB_FUNCTION @@ -1418,6 +1531,12 @@ static const struct { { NULL } }; +/** + * Parse nametype string and return a nametype integer + * + * @ingroup krb5_principal + */ + krb5_error_code krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype) { diff --git a/source4/heimdal/lib/krb5/replay.c b/source4/heimdal/lib/krb5/replay.c index be484c29dc..0cad91e437 100644 --- a/source4/heimdal/lib/krb5/replay.c +++ b/source4/heimdal/lib/krb5/replay.c @@ -133,9 +133,10 @@ krb5_rc_initialize(krb5_context context, int ret; if(f == NULL) { + char buf[128]; ret = errno; - krb5_set_error_message(context, ret, "open(%s): %s", id->name, - strerror(ret)); + strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, "open(%s): %s", id->name, buf); return ret; } tmp.stamp = auth_lifespan; @@ -158,9 +159,10 @@ krb5_rc_destroy(krb5_context context, int ret; if(remove(id->name) < 0) { + char buf[128]; ret = errno; - krb5_set_error_message(context, ret, "remove(%s): %s", id->name, - strerror(ret)); + strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, "remove(%s): %s", id->name, buf); return ret; } return krb5_rc_close(context, id); @@ -208,9 +210,10 @@ krb5_rc_store(krb5_context context, checksum_authenticator(rep, ent.data); f = fopen(id->name, "r"); if(f == NULL) { + char buf[128]; ret = errno; - krb5_set_error_message(context, ret, "open(%s): %s", id->name, - strerror(ret)); + strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, "open(%s): %s", id->name, buf); return ret; } rk_cloexec_file(f); @@ -226,18 +229,21 @@ krb5_rc_store(krb5_context context, } } if(ferror(f)){ + char buf[128]; ret = errno; fclose(f); + strerror_r(ret, buf, sizeof(buf)); krb5_set_error_message(context, ret, "%s: %s", - id->name, strerror(ret)); + id->name, buf); return ret; } fclose(f); f = fopen(id->name, "a"); if(f == NULL) { + char buf[128]; + strerror_r(errno, buf, sizeof(buf)); krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN, - "open(%s): %s", id->name, - strerror(errno)); + "open(%s): %s", id->name, buf); return KRB5_RC_IO_UNKNOWN; } fwrite(&ent, 1, sizeof(ent), f); diff --git a/source4/heimdal/lib/krb5/warn.c b/source4/heimdal/lib/krb5/warn.c index 05239186ec..886a1fe981 100644 --- a/source4/heimdal/lib/krb5/warn.c +++ b/source4/heimdal/lib/krb5/warn.c @@ -59,19 +59,13 @@ _warnerr(krb5_context context, int do_errtext, *arg++ = msg; } if(context && do_errtext){ - const char *err_msg; - strlcat(xfmt, "%s", sizeof(xfmt)); err_str = krb5_get_error_message(context, code); if (err_str != NULL) { *arg = err_str; } else { - err_msg = krb5_get_err_text(context, code); - if (err_msg) - *arg = err_msg; - else - *arg= "<unknown error>"; + *arg= "<unknown error>"; } } diff --git a/source4/heimdal/lib/roken/rkpty.c b/source4/heimdal/lib/roken/rkpty.c index 2776c1318b..6043e2b815 100644 --- a/source4/heimdal/lib/roken/rkpty.c +++ b/source4/heimdal/lib/roken/rkpty.c @@ -93,6 +93,10 @@ caught_signal(int signo) static void open_pty(void) { +#ifdef _AIX + printf("implement open_pty\n"); + exit(77); +#endif #if defined(HAVE_OPENPTY) || defined(__linux) || defined(__osf__) /* XXX */ if(openpty(&master, &slave, line, 0, 0) == 0) return; diff --git a/source4/heimdal/lib/roken/roken.h.in b/source4/heimdal/lib/roken/roken.h.in index 2bd471736c..6fc533c697 100644 --- a/source4/heimdal/lib/roken/roken.h.in +++ b/source4/heimdal/lib/roken/roken.h.in @@ -306,6 +306,12 @@ int ROKEN_LIB_FUNCTION getdtablesize(void); char * ROKEN_LIB_FUNCTION strerror(int); #endif +#if !defined(HAVE_STRERROR) && !defined(strerror) +#define strerror_r rk_strerror_r +int ROKEN_LIB_FUNCTION strerror_r(int, char *, size_t); +#endif + + #if !defined(HAVE_HSTRERROR) || defined(NEED_HSTRERROR_PROTO) #ifndef HAVE_HSTRERROR #define hstrerror rk_hstrerror @@ -476,7 +482,7 @@ unsigned short ROKEN_LIB_FUNCTION bswap16(unsigned short); int rk_flock(int fd, int operation); #endif /* HAVE_FLOCK */ -#ifdef SunOS +#if defined(SunOS) || defined(_AIX) #define dirfd(x) ((x)->dd_fd) #endif @@ -799,6 +805,12 @@ time_t ROKEN_LIB_FUNCTION rk_timegm(struct tm *tm); #endif +#ifdef NEED_QSORT +#define qsort rk_qsort +void +rk_qsort(void *, size_t, size_t, int (*)(const void *, const void *)); +#endif + #ifdef SOCKET_WRAPPER_REPLACE #include <socket_wrapper.h> #endif diff --git a/source4/heimdal/lib/wind/normalize.c b/source4/heimdal/lib/wind/normalize.c index 4c70a52932..102c577e66 100644 --- a/source4/heimdal/lib/wind/normalize.c +++ b/source4/heimdal/lib/wind/normalize.c @@ -39,6 +39,9 @@ #include <assert.h> #include <stdlib.h> #include <errno.h> +#include <stdio.h> + +#include "roken.h" #include "normalize_table.h" @@ -173,7 +176,7 @@ cc_cmp(const void *a, const void *b) static void canonical_reorder(uint32_t *tmp, size_t tmp_len) { - unsigned i; + size_t i; for (i = 0; i < tmp_len; ++i) { int cc = _wind_combining_class(tmp[i]); @@ -183,8 +186,7 @@ canonical_reorder(uint32_t *tmp, size_t tmp_len) j < tmp_len && _wind_combining_class(tmp[j]); ++j) ; - qsort(&tmp[i], j - i, sizeof(unsigned), - cc_cmp); + qsort(&tmp[i], j - i, sizeof(tmp[0]), cc_cmp); i = j; } } @@ -280,6 +282,11 @@ _wind_stringprep_normalize(const uint32_t *in, size_t in_len, uint32_t *tmp; int ret; + if (in_len == 0) { + *out_len = 0; + return 0; + } + tmp_len = in_len * 4; if (tmp_len < MAX_LENGTH_CANON) tmp_len = MAX_LENGTH_CANON; diff --git a/source4/heimdal/lib/wind/stringprep.c b/source4/heimdal/lib/wind/stringprep.c index a991f20cfb..ec4657665e 100644 --- a/source4/heimdal/lib/wind/stringprep.c +++ b/source4/heimdal/lib/wind/stringprep.c @@ -58,10 +58,16 @@ wind_stringprep(const uint32_t *in, size_t in_len, wind_profile_flags flags) { size_t tmp_len = in_len * 3; - uint32_t *tmp = malloc(tmp_len * sizeof(uint32_t)); + uint32_t *tmp; int ret; size_t olen; + if (in_len == 0) { + *out_len = 0; + return 0; + } + + tmp = malloc(tmp_len * sizeof(uint32_t)); if (tmp == NULL) return ENOMEM; |