KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_mk_priv(krb5_context context, krb5_auth_context auth_context, const krb5_data *userdata, krb5_data *outbuf, krb5_replay_data *outdata) { krb5_error_code ret; KRB_PRIV s; EncKrbPrivPart part; u_char *buf = NULL; size_t buf_size; size_t len = 0; krb5_crypto crypto; krb5_keyblock *key; krb5_replay_data rdata; if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && outdata == NULL) return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */ if (auth_context->local_subkey) key = auth_context->local_subkey; else if (auth_context->remote_subkey) key = auth_context->remote_subkey; else key = auth_context->keyblock; memset(&rdata, 0, sizeof(rdata)); part.user_data = *userdata; krb5_us_timeofday (context, &rdata.timestamp, &rdata.usec); if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { part.timestamp = &rdata.timestamp; part.usec = &rdata.usec; } else { part.timestamp = NULL; part.usec = NULL; } if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_TIME) { outdata->timestamp = rdata.timestamp; outdata->usec = rdata.usec; } if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { rdata.seq = auth_context->local_seqnumber; part.seq_number = &rdata.seq; } else part.seq_number = NULL; if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE) outdata->seq = auth_context->local_seqnumber; part.s_address = auth_context->local_address; part.r_address = auth_context->remote_address; krb5_data_zero (&s.enc_part.cipher); ASN1_MALLOC_ENCODE(EncKrbPrivPart, buf, buf_size, &part, &len, ret); if (ret) goto fail; if (buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); s.pvno = 5; s.msg_type = krb_priv; s.enc_part.etype = key->keytype; s.enc_part.kvno = NULL; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free (buf); return ret; } ret = krb5_encrypt (context, crypto, KRB5_KU_KRB_PRIV, buf + buf_size - len, len, &s.enc_part.cipher); krb5_crypto_destroy(context, crypto); if (ret) { free(buf); return ret; } free(buf); ASN1_MALLOC_ENCODE(KRB_PRIV, buf, buf_size, &s, &len, ret); if (ret) goto fail; if (buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); krb5_data_free (&s.enc_part.cipher); ret = krb5_data_copy(outbuf, buf + buf_size - len, len); if (ret) { free(buf); return krb5_enomem(context); } free (buf); if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) auth_context->local_seqnumber = (auth_context->local_seqnumber + 1) & 0xFFFFFFFF; return 0; fail: free (buf); krb5_data_free (&s.enc_part.cipher); return ret; }
static int make_path(krb5_context context, struct tr_realm *r, const char *from, const char *to) { struct tr_realm *tmp; const char *p; if(strlen(from) < strlen(to)){ const char *str; str = from; from = to; to = str; } if(strcmp(from + strlen(from) - strlen(to), to) == 0){ p = from; while(1){ p = strchr(p, '.'); if(p == NULL) { krb5_clear_error_message (context); return KRB5KDC_ERR_POLICY; } p++; if(strcmp(p, to) == 0) break; tmp = calloc(1, sizeof(*tmp)); if(tmp == NULL) return krb5_enomem(context); tmp->next = r->next; r->next = tmp; tmp->realm = strdup(p); if(tmp->realm == NULL){ r->next = tmp->next; free(tmp); return krb5_enomem(context); } } }else if(strncmp(from, to, strlen(to)) == 0){ p = from + strlen(from); while(1){ while(p >= from && *p != '/') p--; if(p == from) return KRB5KDC_ERR_POLICY; if(strncmp(to, from, p - from) == 0) break; tmp = calloc(1, sizeof(*tmp)); if(tmp == NULL) return krb5_enomem(context); tmp->next = r->next; r->next = tmp; tmp->realm = malloc(p - from + 1); if(tmp->realm == NULL){ r->next = tmp->next; free(tmp); return krb5_enomem(context); } memcpy(tmp->realm, from, p - from); tmp->realm[p - from] = '\0'; p--; } } else { krb5_clear_error_message (context); return KRB5KDC_ERR_POLICY; } return 0; }
static krb5_error_code build_logon_name(krb5_context context, time_t authtime, krb5_const_principal principal, krb5_data *logon) { krb5_error_code ret; krb5_storage *sp; uint64_t t; char *s, *s2; size_t s2_len; t = unix2nttime(authtime); krb5_data_zero(logon); sp = krb5_storage_emem(); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out); CHECK(ret, krb5_store_uint32(sp, t >> 32), out); ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM | KRB5_PRINCIPAL_UNPARSE_DISPLAY, &s); if (ret) goto out; { size_t ucs2_len; uint16_t *ucs2; unsigned int flags; ret = wind_utf8ucs2_length(s, &ucs2_len); if (ret) { krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s); free(s); return ret; } ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len); if (ucs2 == NULL) { free(s); return krb5_enomem(context); } ret = wind_utf8ucs2(s, ucs2, &ucs2_len); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s); free(s); return ret; } else free(s); s2_len = (ucs2_len + 1) * 2; s2 = malloc(s2_len); if (s2 == NULL) { free(ucs2); return krb5_enomem(context); } flags = WIND_RW_LE; ret = wind_ucs2write(ucs2, ucs2_len, &flags, s2, &s2_len); free(ucs2); if (ret) { free(s2); krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer"); return ret; } /* * we do not want zero termination */ s2_len = ucs2_len * 2; } CHECK(ret, krb5_store_uint16(sp, s2_len), out); ret = krb5_storage_write(sp, s2, s2_len); free(s2); if (ret != (int)s2_len) { ret = krb5_enomem(context); goto out; } ret = krb5_storage_to_data(sp, logon); if (ret) goto out; krb5_storage_free(sp); return 0; out: krb5_storage_free(sp); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_pac_sign(krb5_context context, krb5_pac p, time_t authtime, krb5_principal principal, const krb5_keyblock *server_key, const krb5_keyblock *priv_key, krb5_data *data) { krb5_error_code ret; krb5_storage *sp = NULL, *spdata = NULL; uint32_t end; size_t server_size, priv_size; uint32_t server_offset = 0, priv_offset = 0; uint32_t server_cksumtype = 0, priv_cksumtype = 0; int num = 0; size_t i; krb5_data logon, d; krb5_data_zero(&logon); for (i = 0; i < p->pac->numbuffers; i++) { if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { if (p->server_checksum == NULL) { p->server_checksum = &p->pac->buffers[i]; } if (p->server_checksum != &p->pac->buffers[i]) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two server checksums", "")); goto out; } } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { if (p->privsvr_checksum == NULL) { p->privsvr_checksum = &p->pac->buffers[i]; } if (p->privsvr_checksum != &p->pac->buffers[i]) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two KDC checksums", "")); goto out; } } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { if (p->logon_name == NULL) { p->logon_name = &p->pac->buffers[i]; } if (p->logon_name != &p->pac->buffers[i]) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two logon names", "")); goto out; } } } if (p->logon_name == NULL) num++; if (p->server_checksum == NULL) num++; if (p->privsvr_checksum == NULL) num++; if (num) { void *ptr; ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1))); if (ptr == NULL) return krb5_enomem(context); p->pac = ptr; if (p->logon_name == NULL) { p->logon_name = &p->pac->buffers[p->pac->numbuffers++]; memset(p->logon_name, 0, sizeof(*p->logon_name)); p->logon_name->type = PAC_LOGON_NAME; } if (p->server_checksum == NULL) { p->server_checksum = &p->pac->buffers[p->pac->numbuffers++]; memset(p->server_checksum, 0, sizeof(*p->server_checksum)); p->server_checksum->type = PAC_SERVER_CHECKSUM; } if (p->privsvr_checksum == NULL) { p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++]; memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM; } } /* Calculate LOGON NAME */ ret = build_logon_name(context, authtime, principal, &logon); if (ret) goto out; /* Set lengths for checksum */ ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); if (ret) goto out; ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); if (ret) goto out; /* Encode PAC */ sp = krb5_storage_emem(); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); spdata = krb5_storage_emem(); if (spdata == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out); CHECK(ret, krb5_store_uint32(sp, p->pac->version), out); end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); for (i = 0; i < p->pac->numbuffers; i++) { uint32_t len; size_t sret; void *ptr = NULL; /* store data */ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { len = server_size + 4; server_offset = end + 4; CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out); CHECK(ret, fill_zeros(context, spdata, server_size), out); } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { len = priv_size + 4; priv_offset = end + 4; CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out); CHECK(ret, fill_zeros(context, spdata, priv_size), out); } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { len = krb5_storage_write(spdata, logon.data, logon.length); if (logon.length != len) { ret = EINVAL; goto out; } } else { len = p->pac->buffers[i].buffersize; ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; sret = krb5_storage_write(spdata, ptr, len); if (sret != len) { ret = krb5_enomem(context); goto out; } /* XXX if not aligned, fill_zeros */ } /* write header */ CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out); CHECK(ret, krb5_store_uint32(sp, len), out); CHECK(ret, krb5_store_uint32(sp, end), out); CHECK(ret, krb5_store_uint32(sp, 0), out); /* advance data endpointer and align */ { int32_t e; end += len; e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; if ((int32_t)end != e) { CHECK(ret, fill_zeros(context, spdata, e - end), out); } end = e; } } /* assert (server_offset != 0 && priv_offset != 0); */ /* export PAC */ ret = krb5_storage_to_data(spdata, &d); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } ret = krb5_storage_write(sp, d.data, d.length); if (ret != (int)d.length) { krb5_data_free(&d); ret = krb5_enomem(context); goto out; } krb5_data_free(&d); ret = krb5_storage_to_data(sp, &d); if (ret) { ret = krb5_enomem(context); goto out; } /* sign */ ret = create_checksum(context, server_key, server_cksumtype, d.data, d.length, (char *)d.data + server_offset, server_size); if (ret) { krb5_data_free(&d); goto out; } ret = create_checksum(context, priv_key, priv_cksumtype, (char *)d.data + server_offset, server_size, (char *)d.data + priv_offset, priv_size); if (ret) { krb5_data_free(&d); goto out; } /* done */ *data = d; krb5_data_free(&logon); krb5_storage_free(sp); krb5_storage_free(spdata); return 0; out: krb5_data_free(&logon); if (sp) krb5_storage_free(sp); if (spdata) krb5_storage_free(spdata); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_copy_context(krb5_context context, krb5_context *out) { krb5_error_code ret; krb5_context p; *out = NULL; p = calloc(1, sizeof(*p)); if (p == NULL) return krb5_enomem(context); p->mutex = malloc(sizeof(HEIMDAL_MUTEX)); if (p->mutex == NULL) { free(p); return krb5_enomem(context); } HEIMDAL_MUTEX_init(p->mutex); if (context->default_cc_name) p->default_cc_name = strdup(context->default_cc_name); if (context->default_cc_name_env) p->default_cc_name_env = strdup(context->default_cc_name_env); if (context->etypes) { ret = copy_etypes(context, context->etypes, &p->etypes); if (ret) goto out; } if (context->etypes_des) { ret = copy_etypes(context, context->etypes_des, &p->etypes_des); if (ret) goto out; } if (context->default_realms) { ret = krb5_copy_host_realm(context, context->default_realms, &p->default_realms); if (ret) goto out; } ret = _krb5_config_copy(context, context->cf, &p->cf); if (ret) goto out; /* XXX should copy */ krb5_init_ets(p); cc_ops_copy(p, context); kt_ops_copy(p, context); #if 0 /* XXX */ if(context->warn_dest != NULL) ; if(context->debug_dest != NULL) ; #endif ret = krb5_set_extra_addresses(p, context->extra_addresses); if (ret) goto out; ret = krb5_set_extra_addresses(p, context->ignore_addresses); if (ret) goto out; ret = _krb5_copy_send_to_kdc_func(p, context); if (ret) goto out; *out = p; return 0; out: krb5_free_context(p); return ret; }
static krb5_error_code srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count, const char *realm, const char *dns_type, const char *proto, const char *service, int port) { char domain[1024]; struct rk_dns_reply *r; struct rk_resource_record *rr; int num_srv; int proto_num; int def_port; *res = NULL; *count = 0; proto_num = string_to_proto(proto); if(proto_num < 0) { krb5_set_error_message(context, EINVAL, N_("unknown protocol `%s' to lookup", ""), proto); return EINVAL; } if(proto_num == KRB5_KRBHST_HTTP) def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); else if(port == 0) def_port = ntohs(krb5_getportbyname (context, service, proto, 88)); else def_port = port; snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm); r = rk_dns_lookup(domain, dns_type); if(r == NULL) { _krb5_debug(context, 0, "DNS lookup failed domain: %s", domain); return KRB5_KDC_UNREACH; } for(num_srv = 0, rr = r->head; rr; rr = rr->next) if(rr->type == rk_ns_t_srv) num_srv++; *res = malloc(num_srv * sizeof(**res)); if(*res == NULL) { rk_dns_free_data(r); return krb5_enomem(context); } rk_dns_srv_order(r); for(num_srv = 0, rr = r->head; rr; rr = rr->next) if(rr->type == rk_ns_t_srv) { krb5_krbhst_info *hi; size_t len = strlen(rr->u.srv->target); hi = calloc(1, sizeof(*hi) + len); if(hi == NULL) { rk_dns_free_data(r); while(--num_srv >= 0) free((*res)[num_srv]); free(*res); *res = NULL; return krb5_enomem(context); } (*res)[num_srv++] = hi; hi->proto = proto_num; hi->def_port = def_port; if (port != 0) hi->port = port; else hi->port = rr->u.srv->port; strlcpy(hi->hostname, rr->u.srv->target, len + 1); } *count = num_srv; rk_dns_free_data(r); return 0; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_parse(krb5_context context, const void *ptr, size_t len, krb5_pac *pac) { krb5_error_code ret; krb5_pac p; krb5_storage *sp = NULL; uint32_t i, tmp, tmp2, header_end; p = calloc(1, sizeof(*p)); if (p == NULL) { ret = krb5_enomem(context); goto out; } sp = krb5_storage_from_readonly_mem(ptr, len); if (sp == NULL) { ret = krb5_enomem(context); goto out; } krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &tmp), out); CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); if (tmp < 1) { ret = EINVAL; /* Too few buffers */ krb5_set_error_message(context, ret, N_("PAC have too few buffer", "")); goto out; } if (tmp2 != 0) { ret = EINVAL; /* Wrong version */ krb5_set_error_message(context, ret, N_("PAC have wrong version %d", ""), (int)tmp2); goto out; } p->pac = calloc(1, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); if (p->pac == NULL) { ret = krb5_enomem(context); goto out; } p->pac->numbuffers = tmp; p->pac->version = tmp2; header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); if (header_end > len) { ret = EINVAL; goto out; } for (i = 0; i < p->pac->numbuffers; i++) { CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); /* consistency checks */ if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC out of allignment", "")); goto out; } if (p->pac->buffers[i].offset_hi) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC high offset set", "")); goto out; } if (p->pac->buffers[i].offset_lo > len) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC offset off end", "")); goto out; } if (p->pac->buffers[i].offset_lo < header_end) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC offset inside header: %lu %lu", ""), (unsigned long)p->pac->buffers[i].offset_lo, (unsigned long)header_end); goto out; } if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC length off end", "")); goto out; } /* let save pointer to data we need later */ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { if (p->server_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two server checksums", "")); goto out; } p->server_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { if (p->privsvr_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two KDC checksums", "")); goto out; } p->privsvr_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { if (p->logon_name) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two logon names", "")); goto out; } p->logon_name = &p->pac->buffers[i]; } } ret = krb5_data_copy(&p->data, ptr, len); if (ret) goto out; krb5_storage_free(sp); *pac = p; return 0; out: if (sp) krb5_storage_free(sp); if (p) { if (p->pac) free(p->pac); free(p); } *pac = NULL; return ret; }
krb5_error_code _kdc_db_fetch(krb5_context context, krb5_kdc_configuration *config, krb5_const_principal principal, unsigned flags, krb5int32 *kvno_ptr, HDB **db, hdb_entry_ex **h) { hdb_entry_ex *ent; krb5_error_code ret = HDB_ERR_NOENTRY; int i; unsigned kvno = 0; krb5_principal enterprise_principal = NULL; krb5_const_principal princ; *h = NULL; if (kvno_ptr) { kvno = *kvno_ptr; flags |= HDB_F_KVNO_SPECIFIED; } ent = calloc(1, sizeof (*ent)); if (ent == NULL) return krb5_enomem(context); if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { if (principal->name.name_string.len != 1) { ret = KRB5_PARSE_MALFORMED; krb5_set_error_message(context, ret, "malformed request: " "enterprise name with %d name components", principal->name.name_string.len); goto out; } ret = krb5_parse_name(context, principal->name.name_string.val[0], &enterprise_principal); if (ret) goto out; } for (i = 0; i < config->num_db; i++) { ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to open database: %s", msg); krb5_free_error_message(context, msg); continue; } princ = principal; if (!(config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal) princ = enterprise_principal; ret = config->db[i]->hdb_fetch_kvno(context, config->db[i], princ, flags | HDB_F_DECRYPT, kvno, ent); config->db[i]->hdb_close(context, config->db[i]); switch (ret) { case HDB_ERR_WRONG_REALM: /* * the ent->entry.principal just contains hints for the client * to retry. This is important for enterprise principal routing * between trusts. */ /* fall through */ case 0: if (db) *db = config->db[i]; *h = ent; ent = NULL; goto out; case HDB_ERR_NOENTRY: /* Check the other databases */ continue; default: /* * This is really important, because errors like * HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that * the RODC on which this code is running does not have * the key we need, and so a proxy to the KDC is required) * have specific meaning, and need to be propogated up. */ goto out; } } if (ret == HDB_ERR_NOENTRY) { krb5_set_error_message(context, ret, "no such entry found in hdb"); } out: krb5_free_principal(context, enterprise_principal); free(ent); return ret; }
static krb5_error_code KRB5_CALLCONV akf_add_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) { struct akf_data *d = id->data; int fd, created = 0; krb5_error_code ret; int32_t len; krb5_storage *sp; if (entry->keyblock.keyvalue.length != 8) return 0; switch(entry->keyblock.keytype) { case ETYPE_DES_CBC_CRC: case ETYPE_DES_CBC_MD4: case ETYPE_DES_CBC_MD5: break; default: return 0; } fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC); if (fd < 0) { fd = open (d->filename, O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); if (fd < 0) { ret = errno; krb5_set_error_message(context, ret, N_("open keyfile(%s): %s", ""), d->filename, strerror(ret)); return ret; } created = 1; } sp = krb5_storage_from_fd(fd); if(sp == NULL) { close(fd); return krb5_enomem(context); } if (created) len = 0; else { if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { ret = errno; krb5_storage_free(sp); close(fd); krb5_set_error_message(context, ret, N_("seeking in keyfile: %s", ""), strerror(ret)); return ret; } ret = krb5_ret_int32(sp, &len); if(ret) { krb5_storage_free(sp); close(fd); return ret; } } /* * Make sure we don't add the entry twice, assumes the DES * encryption types are all the same key. */ if (len > 0) { int32_t kvno; int i; for (i = 0; i < len; i++) { ret = krb5_ret_int32(sp, &kvno); if (ret) { krb5_set_error_message (context, ret, N_("Failed getting kvno from keyfile", "")); goto out; } if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) { ret = errno; krb5_set_error_message (context, ret, N_("Failed seeing in keyfile: %s", ""), strerror(ret)); goto out; } if (kvno == entry->vno) { ret = 0; goto out; } } } len++; if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { ret = errno; krb5_set_error_message (context, ret, N_("Failed seeing in keyfile: %s", ""), strerror(ret)); goto out; } ret = krb5_store_int32(sp, len); if(ret) { ret = errno; krb5_set_error_message (context, ret, N_("keytab keyfile failed new length", "")); return ret; } if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) { ret = errno; krb5_set_error_message (context, ret, N_("seek to end: %s", ""), strerror(ret)); goto out; } ret = krb5_store_int32(sp, entry->vno); if(ret) { krb5_set_error_message(context, ret, N_("keytab keyfile failed store kvno", "")); goto out; } ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, entry->keyblock.keyvalue.length); if(ret != entry->keyblock.keyvalue.length) { if (ret < 0) ret = errno; else ret = ENOTTY; krb5_set_error_message(context, ret, N_("keytab keyfile failed to add key", "")); goto out; } ret = 0; out: krb5_storage_free(sp); close (fd); return ret; }
/** * Internal function to expand tokens in paths. * * Inputs: * * @context A krb5_context * @path_in The path to expand tokens from * @ppath_out The expanded path * @... Variable number of pairs of strings, the first of each * being a token (e.g., "luser") and the second a string to * replace it with. The list is terminated by a NULL. * * Outputs: * * @ppath_out Path with expanded tokens (caller must free() this) */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_expand_path_tokensv(krb5_context context, const char *path_in, char **ppath_out, ...) { char *tok_begin, *tok_end, *append; char **extra_tokens = NULL; const char *path_left; const char *s; size_t nargs = 0; size_t len = 0; va_list ap; if (path_in == NULL || *path_in == '\0') { *ppath_out = strdup(""); return 0; } *ppath_out = NULL; va_start(ap, ppath_out); while ((s = va_arg(ap, const char *))) { nargs++; s = va_arg(ap, const char *); } va_end(ap); nargs *= 2; /* Get extra tokens */ if (nargs) { size_t i; extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens)); if (extra_tokens == NULL) return krb5_enomem(context); va_start(ap, ppath_out); for (i = 0; i < nargs; i++) { s = va_arg(ap, const char *); /* token key */ if (s == NULL) break; extra_tokens[i] = strdup(s); if (extra_tokens[i++] == NULL) { free_extra_tokens(extra_tokens); return krb5_enomem(context); } s = va_arg(ap, const char *); /* token value */ if (s == NULL) s = ""; extra_tokens[i] = strdup(s); if (extra_tokens[i] == NULL) { free_extra_tokens(extra_tokens); return krb5_enomem(context); } } va_end(ap); } for (path_left = path_in; path_left && *path_left; ) { tok_begin = strstr(path_left, "%{"); if (tok_begin && tok_begin != path_left) { append = malloc((tok_begin - path_left) + 1); if (append) { memcpy(append, path_left, tok_begin - path_left); append[tok_begin - path_left] = '\0'; } path_left = tok_begin; } else if (tok_begin) { tok_end = strchr(tok_begin, '}'); if (tok_end == NULL) { free_extra_tokens(extra_tokens); if (*ppath_out) free(*ppath_out); *ppath_out = NULL; if (context) krb5_set_error_message(context, EINVAL, "variable missing }"); return EINVAL; } if (_expand_token(context, tok_begin, tok_end, extra_tokens, &append)) { free_extra_tokens(extra_tokens); if (*ppath_out) free(*ppath_out); *ppath_out = NULL; return EINVAL; } path_left = tok_end + 1; } else { append = strdup(path_left); path_left = NULL; } if (append == NULL) { free_extra_tokens(extra_tokens); if (*ppath_out) free(*ppath_out); *ppath_out = NULL; return krb5_enomem(context); } { size_t append_len = strlen(append); char * new_str = realloc(*ppath_out, len + append_len + 1); if (new_str == NULL) { free_extra_tokens(extra_tokens); free(append); if (*ppath_out) free(*ppath_out); *ppath_out = NULL; return krb5_enomem(context); } *ppath_out = new_str; memcpy(*ppath_out + len, append, append_len + 1); len = len + append_len; free(append); } } #ifdef _WIN32 /* Also deal with slashes */ if (*ppath_out) { char * c; for (c = *ppath_out; *c; c++) if (*c == '/') *c = '\\'; } #endif free_extra_tokens(extra_tokens); return 0; }
static krb5_error_code AES_SHA2_string_to_key(krb5_context context, krb5_enctype enctype, krb5_data password, krb5_salt salt, krb5_data opaque, krb5_keyblock *key) { krb5_error_code ret; uint32_t iter; struct _krb5_encryption_type *et = NULL; struct _krb5_key_data kd; krb5_data saltp; size_t enctypesz; const EVP_MD *md = NULL; krb5_data_zero(&saltp); kd.key = NULL; kd.schedule = NULL; if (opaque.length == 0) { iter = _krb5_AES_SHA2_string_to_default_iterator; } else if (opaque.length == 4) { unsigned long v; _krb5_get_int(opaque.data, &v, 4); iter = ((uint32_t)v); } else { ret = KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */ goto cleanup; } et = _krb5_find_enctype(enctype); if (et == NULL) { ret = KRB5_PROG_KEYTYPE_NOSUPP; goto cleanup; } kd.schedule = NULL; ALLOC(kd.key, 1); if (kd.key == NULL) { ret = krb5_enomem(context); goto cleanup; } kd.key->keytype = enctype; ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size); if (ret) { ret = krb5_enomem(context); goto cleanup; } enctypesz = strlen(et->name) + 1; ret = krb5_data_alloc(&saltp, enctypesz + salt.saltvalue.length); if (ret) { ret = krb5_enomem(context); goto cleanup; } memcpy(saltp.data, et->name, enctypesz); memcpy((unsigned char *)saltp.data + enctypesz, salt.saltvalue.data, salt.saltvalue.length); ret = _krb5_aes_sha2_md_for_enctype(context, enctype, &md); if (ret) goto cleanup; ret = PKCS5_PBKDF2_HMAC(password.data, password.length, saltp.data, saltp.length, iter, md, et->keytype->size, kd.key->keyvalue.data); if (ret != 1) { krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, "Error calculating s2k"); ret = KRB5_PROG_KEYTYPE_NOSUPP; goto cleanup; } ret = _krb5_derive_key(context, et, &kd, "kerberos", strlen("kerberos")); if (ret) goto cleanup; ret = krb5_copy_keyblock_contents(context, kd.key, key); if (ret) goto cleanup; cleanup: krb5_data_free(&saltp); _krb5_free_key_data(context, &kd, et); return ret; }
static krb5_error_code kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) { krb5_error_code ret; krb5_kcm_cursor c; krb5_storage *request, *response; krb5_data response_data; *cursor = NULL; c = calloc(1, sizeof(*c)); if (c == NULL) { ret = krb5_enomem(context); goto out; } ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request); if (ret) goto out; ret = krb5_kcm_call(context, request, &response, &response_data); krb5_storage_free(request); if (ret) goto out; while (1) { ssize_t sret; kcmuuid_t uuid; void *ptr; sret = krb5_storage_read(response, &uuid, sizeof(uuid)); if (sret == 0) { ret = 0; break; } else if (sret != sizeof(uuid)) { ret = EINVAL; goto out; } ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); if (ptr == NULL) { ret = krb5_enomem(context); goto out; } c->uuids = ptr; memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); c->length += 1; } krb5_storage_free(response); krb5_data_free(&response_data); out: if (ret && c) { free(c->uuids); free(c); } else *cursor = c; return ret; }
static krb5_error_code verify_logonname(krb5_context context, const struct PAC_INFO_BUFFER *logon_name, const krb5_data *data, time_t authtime, krb5_const_principal principal) { krb5_error_code ret; krb5_principal p2; uint32_t time1, time2; krb5_storage *sp; uint16_t len; char *s; sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, logon_name->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &time1), out); CHECK(ret, krb5_ret_uint32(sp, &time2), out); { uint64_t t1, t2; t1 = unix2nttime(authtime); t2 = ((uint64_t)time2 << 32) | time1; if (t1 != t2) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch"); return EINVAL; } } CHECK(ret, krb5_ret_uint16(sp, &len), out); if (len == 0) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC logon name length missing"); return EINVAL; } s = malloc(len); if (s == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } ret = krb5_storage_read(sp, s, len); if (ret != len) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name"); return EINVAL; } krb5_storage_free(sp); { size_t ucs2len = len / 2; uint16_t *ucs2; size_t u8len; unsigned int flags = WIND_RW_LE; ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); if (ucs2 == NULL) return krb5_enomem(context); ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); free(s); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); return ret; } ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string"); return ret; } u8len += 1; /* Add space for NUL */ s = malloc(u8len); if (s == NULL) { free(ucs2); return krb5_enomem(context); } ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len); free(ucs2); if (ret) { free(s); krb5_set_error_message(context, ret, "Failed to convert to UTF-8"); return ret; } } ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); free(s); if (ret) return ret; if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC logon name mismatch"); } krb5_free_principal(context, p2); return ret; out: return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_ap_req2(krb5_context context, krb5_auth_context *auth_context, krb5_ap_req *ap_req, krb5_const_principal server, krb5_keyblock *keyblock, krb5_flags flags, krb5_flags *ap_req_options, krb5_ticket **ticket, krb5_key_usage usage) { krb5_ticket *t; krb5_auth_context ac; krb5_error_code ret; EtypeList etypes; memset(&etypes, 0, sizeof(etypes)); if (ticket) *ticket = NULL; if (auth_context && *auth_context) { ac = *auth_context; } else { ret = krb5_auth_con_init (context, &ac); if (ret) return ret; } t = calloc(1, sizeof(*t)); if (t == NULL) { ret = krb5_enomem(context); goto out; } if (ap_req->ap_options.use_session_key && ac->keyblock){ ret = krb5_decrypt_ticket(context, &ap_req->ticket, ac->keyblock, &t->ticket, flags); krb5_free_keyblock(context, ac->keyblock); ac->keyblock = NULL; }else ret = krb5_decrypt_ticket(context, &ap_req->ticket, keyblock, &t->ticket, flags); if(ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->server, ap_req->ticket.sname, ap_req->ticket.realm); if (ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->client, t->ticket.cname, t->ticket.crealm); if (ret) goto out; ret = decrypt_authenticator (context, &t->ticket.key, &ap_req->authenticator, ac->authenticator, usage); if (ret) goto out; { krb5_principal p1, p2; krb5_boolean res; _krb5_principalname2krb5_principal(context, &p1, ac->authenticator->cname, ac->authenticator->crealm); _krb5_principalname2krb5_principal(context, &p2, t->ticket.cname, t->ticket.crealm); res = krb5_principal_compare (context, p1, p2); krb5_free_principal (context, p1); krb5_free_principal (context, p2); if (!res) { ret = KRB5KRB_AP_ERR_BADMATCH; krb5_clear_error_message (context); goto out; } } /* check addresses */ if (t->ticket.caddr && ac->remote_address && !krb5_address_search (context, ac->remote_address, t->ticket.caddr)) { ret = KRB5KRB_AP_ERR_BADADDR; krb5_clear_error_message (context); goto out; } /* check timestamp in authenticator */ { krb5_timestamp now; krb5_timeofday (context, &now); if (labs(ac->authenticator->ctime - now) > context->max_skew) { ret = KRB5KRB_AP_ERR_SKEW; krb5_clear_error_message (context); goto out; } } if (ac->authenticator->seq_number) krb5_auth_con_setremoteseqnumber(context, ac, *ac->authenticator->seq_number); /* XXX - Xor sequence numbers */ if (ac->authenticator->subkey) { ret = krb5_auth_con_setremotesubkey(context, ac, ac->authenticator->subkey); if (ret) goto out; } ret = find_etypelist(context, ac, &etypes); if (ret) goto out; ac->keytype = ETYPE_NULL; if (etypes.val) { size_t i; for (i = 0; i < etypes.len; i++) { if (krb5_enctype_valid(context, etypes.val[i]) == 0) { ac->keytype = etypes.val[i]; break; } } } /* save key */ ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock); if (ret) goto out; if (ap_req_options) { *ap_req_options = 0; if (ac->keytype != (krb5_enctype)ETYPE_NULL) *ap_req_options |= AP_OPTS_USE_SUBKEY; if (ap_req->ap_options.use_session_key) *ap_req_options |= AP_OPTS_USE_SESSION_KEY; if (ap_req->ap_options.mutual_required) *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; } if(ticket) *ticket = t; else krb5_free_ticket (context, t); if (auth_context) { if (*auth_context == NULL) *auth_context = ac; } else krb5_auth_con_free (context, ac); free_EtypeList(&etypes); return 0; out: free_EtypeList(&etypes); if (t) krb5_free_ticket (context, t); if (auth_context == NULL || *auth_context == NULL) krb5_auth_con_free (context, ac); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_add_buffer(krb5_context context, krb5_pac p, uint32_t type, const krb5_data *data) { krb5_error_code ret; void *ptr; size_t len, offset, header_end, old_end; uint32_t i; len = p->pac->numbuffers; ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len)); if (ptr == NULL) return krb5_enomem(context); p->pac = ptr; for (i = 0; i < len; i++) p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE; offset = p->data.length + PAC_INFO_BUFFER_SIZE; p->pac->buffers[len].type = type; p->pac->buffers[len].buffersize = data->length; p->pac->buffers[len].offset_lo = offset; p->pac->buffers[len].offset_hi = 0; old_end = p->data.length; len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE; if (len < p->data.length) { krb5_set_error_message(context, EINVAL, "integer overrun"); return EINVAL; } /* align to PAC_ALIGNMENT */ len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; ret = krb5_data_realloc(&p->data, len); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); return ret; } /* * make place for new PAC INFO BUFFER header */ header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE, (unsigned char *)p->data.data + header_end , old_end - header_end); memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE); /* * copy in new data part */ memcpy((unsigned char *)p->data.data + offset, data->data, data->length); memset((unsigned char *)p->data.data + offset + data->length, 0, p->data.length - offset - data->length); p->pac->numbuffers += 1; return 0; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_ctx(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_rd_req_in_ctx inctx, krb5_rd_req_out_ctx *outctx) { krb5_error_code ret; krb5_ap_req ap_req; krb5_rd_req_out_ctx o = NULL; krb5_keytab id = NULL, keytab = NULL; krb5_principal service = NULL; *outctx = NULL; o = calloc(1, sizeof(*o)); if (o == NULL) return krb5_enomem(context); if (*auth_context == NULL) { ret = krb5_auth_con_init(context, auth_context); if (ret) goto out; } ret = krb5_decode_ap_req(context, inbuf, &ap_req); if(ret) goto out; /* Save the principal that was in the request */ ret = _krb5_principalname2krb5_principal(context, &o->server, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) goto out; if (ap_req.ap_options.use_session_key && (*auth_context)->keyblock == NULL) { ret = KRB5KRB_AP_ERR_NOKEY; krb5_set_error_message(context, ret, N_("krb5_rd_req: user to user auth " "without session key given", "")); goto out; } if (inctx && inctx->keytab) id = inctx->keytab; if((*auth_context)->keyblock){ ret = krb5_copy_keyblock(context, (*auth_context)->keyblock, &o->keyblock); if (ret) goto out; } else if(inctx && inctx->keyblock){ ret = krb5_copy_keyblock(context, inctx->keyblock, &o->keyblock); if (ret) goto out; } else { if(id == NULL) { krb5_kt_default(context, &keytab); id = keytab; } if (id == NULL) goto out; if (server == NULL) { ret = _krb5_principalname2krb5_principal(context, &service, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) goto out; server = service; } ret = get_key_from_keytab(context, &ap_req, server, id, &o->keyblock); if (ret) { /* If caller specified a server, fail. */ if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0) goto out; /* Otherwise, fall back to iterating over the keytab. This * have serious performace issues for larger keytab. */ o->keyblock = NULL; } } if (o->keyblock) { /* * We got an exact keymatch, use that. */ ret = krb5_verify_ap_req2(context, auth_context, &ap_req, server, o->keyblock, 0, &o->ap_req_options, &o->ticket, KRB5_KU_AP_REQ_AUTH); if (ret) goto out; } else { /* * Interate over keytab to find a key that can decrypt the request. */ krb5_keytab_entry entry; krb5_kt_cursor cursor; int done = 0, kvno = 0; memset(&cursor, 0, sizeof(cursor)); if (ap_req.ticket.enc_part.kvno) kvno = *ap_req.ticket.enc_part.kvno; ret = krb5_kt_start_seq_get(context, id, &cursor); if (ret) goto out; done = 0; while (!done) { krb5_principal p; ret = krb5_kt_next_entry(context, id, &entry, &cursor); if (ret) { _krb5_kt_principal_not_found(context, ret, id, o->server, ap_req.ticket.enc_part.etype, kvno); break; } if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) { krb5_kt_free_entry (context, &entry); continue; } ret = krb5_verify_ap_req2(context, auth_context, &ap_req, server, &entry.keyblock, 0, &o->ap_req_options, &o->ticket, KRB5_KU_AP_REQ_AUTH); if (ret) { krb5_kt_free_entry (context, &entry); continue; } /* * Found a match, save the keyblock for PAC processing, * and update the service principal in the ticket to match * whatever is in the keytab. */ ret = krb5_copy_keyblock(context, &entry.keyblock, &o->keyblock); if (ret) { krb5_kt_free_entry (context, &entry); break; } ret = krb5_copy_principal(context, entry.principal, &p); if (ret) { krb5_kt_free_entry (context, &entry); break; } krb5_free_principal(context, o->ticket->server); o->ticket->server = p; krb5_kt_free_entry (context, &entry); done = 1; } krb5_kt_end_seq_get (context, id, &cursor); if (ret) goto out; } /* If there is a PAC, verify its server signature */ if (inctx == NULL || inctx->check_pac) { krb5_pac pac; krb5_data data; ret = krb5_ticket_get_authorization_data_type(context, o->ticket, KRB5_AUTHDATA_WIN2K_PAC, &data); if (ret == 0) { ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_data_free(&data); if (ret) goto out; ret = krb5_pac_verify(context, pac, o->ticket->ticket.authtime, o->ticket->client, o->keyblock, NULL); krb5_pac_free(context, pac); if (ret) goto out; } else ret = 0; } out: if (ret || outctx == NULL) { krb5_rd_req_out_ctx_free(context, o); } else *outctx = o; free_AP_REQ(&ap_req); if (service) krb5_free_principal(context, service); if (keytab) krb5_kt_close(context, keytab); return ret; }
static krb5_error_code verify_checksum(krb5_context context, const struct PAC_INFO_BUFFER *sig, const krb5_data *data, void *ptr, size_t len, const krb5_keyblock *key) { krb5_storage *sp = NULL; uint32_t type; krb5_error_code ret; Checksum cksum; memset(&cksum, 0, sizeof(cksum)); sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo, sig->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &type), out); cksum.cksumtype = type; cksum.checksum.length = sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); cksum.checksum.data = malloc(cksum.checksum.length); if (cksum.checksum.data == NULL) { ret = krb5_enomem(context); goto out; } ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); if (ret != (int)cksum.checksum.length) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC checksum missing checksum"); goto out; } if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { ret = EINVAL; krb5_set_error_message(context, ret, "Checksum type %d not keyed", cksum.cksumtype); goto out; } /* If the checksum is HMAC-MD5, the checksum type is not tied to * the key type, instead the HMAC-MD5 checksum is applied blindly * on whatever key is used for this connection, avoiding issues * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743 * for the same issue in MIT, and * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx * for Microsoft's explaination */ if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) { Checksum local_checksum; memset(&local_checksum, 0, sizeof(local_checksum)); ret = HMAC_MD5_any_checksum(context, key, ptr, len, KRB5_KU_OTHER_CKSUM, &local_checksum); if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; krb5_set_error_message(context, ret, N_("PAC integrity check failed for " "hmac-md5 checksum", "")); } krb5_data_free(&local_checksum.checksum); } else { krb5_crypto crypto = NULL; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, ptr, len, &cksum); krb5_crypto_destroy(context, crypto); } free(cksum.checksum.data); krb5_storage_free(sp); return ret; out: if (cksum.checksum.data) free(cksum.checksum.data); if (sp) krb5_storage_free(sp); return ret; }
static krb5_error_code KRB5_CALLCONV xcc_store_cred(krb5_context context, krb5_ccache id, krb5_creds *creds) { krb5_xcc *x = XCACHE(id); krb5_storage *sp = NULL; CFDataRef dref = NULL; krb5_data data; CFStringRef principal = NULL; CFDictionaryRef query = NULL; krb5_error_code ret; CFBooleanRef is_tgt = kCFBooleanFalse; CFDateRef authtime = NULL; krb5_data_zero(&data); if (creds->times.starttime) { authtime = CFDateCreate(NULL, (CFTimeInterval)creds->times.starttime - kCFAbsoluteTimeIntervalSince1970); } else if (creds->times.authtime) { authtime = CFDateCreate(NULL, (CFTimeInterval)creds->times.authtime - kCFAbsoluteTimeIntervalSince1970); } else { authtime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); } if (authtime == NULL) { ret = krb5_enomem(context); goto out; } sp = krb5_storage_emem(); if (sp == NULL) { ret = krb5_enomem(context); goto out; } ret = krb5_store_creds(sp, creds); if (ret) goto out; krb5_storage_to_data(sp, &data); dref = CFDataCreateWithBytesNoCopy(NULL, data.data, data.length, kCFAllocatorNull); if (dref == NULL) { ret = krb5_enomem(context); goto out; } if (krb5_principal_is_root_krbtgt(context, creds->server)) is_tgt = kCFBooleanTrue; principal = CFStringCreateFromPrincipal(context, creds->server); if (principal == NULL) { ret = krb5_enomem(context); goto out; } const void *add_keys[] = { (void *)kHEIMObjectType, kHEIMAttrType, kHEIMAttrClientName, kHEIMAttrServerName, kHEIMAttrData, kHEIMAttrParentCredential, kHEIMAttrLeadCredential, kHEIMAttrAuthTime, }; const void *add_values[] = { (void *)kHEIMObjectKerberos, kHEIMTypeKerberos, x->clientName, principal, dref, x->uuid, is_tgt, authtime, }; query = CFDictionaryCreate(NULL, add_keys, add_values, sizeof(add_keys) / sizeof(add_keys[0]), NULL, NULL); heim_assert(query != NULL, "Failed to create dictionary"); HeimCredRef ccred = HeimCredCreate(query, NULL); if (ccred) { CFRelease(ccred); } else { _krb5_debugx(context, 5, "failed to add credential to %s\n", x->cache_name); ret = EINVAL; krb5_set_error_message(context, ret, "failed to store credential to %s", x->cache_name); goto out; } out: if (sp) krb5_storage_free(sp); CFRELEASE_NULL(query); CFRELEASE_NULL(dref); CFRELEASE_NULL(principal); CFRELEASE_NULL(authtime); krb5_data_free(&data); return ret; }
static krb5_error_code verify_logonname(krb5_context context, const struct PAC_INFO_BUFFER *logon_name, const krb5_data *data, time_t authtime, krb5_const_principal principal) { krb5_error_code ret; uint32_t time1, time2; krb5_storage *sp; uint16_t len; char *s = NULL; char *principal_string = NULL; char *logon_string = NULL; sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, logon_name->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &time1), out); CHECK(ret, krb5_ret_uint32(sp, &time2), out); { uint64_t t1, t2; t1 = unix2nttime(authtime); t2 = ((uint64_t)time2 << 32) | time1; /* * When neither the ticket nor the PAC set an explicit authtime, * both times are zero, but relative to different time scales. * So we must compare "not set" values without converting to a * common time reference. */ if (t1 != t2 && (t2 != 0 && authtime != 0)) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch"); return EINVAL; } } CHECK(ret, krb5_ret_uint16(sp, &len), out); if (len == 0) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC logon name length missing"); return EINVAL; } s = malloc(len); if (s == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } ret = krb5_storage_read(sp, s, len); if (ret != len) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name"); return EINVAL; } krb5_storage_free(sp); { size_t ucs2len = len / 2; uint16_t *ucs2; size_t u8len; unsigned int flags = WIND_RW_LE; ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); if (ucs2 == NULL) return krb5_enomem(context); ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); free(s); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); return ret; } ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string"); return ret; } u8len += 1; /* Add space for NUL */ logon_string = malloc(u8len); if (logon_string == NULL) { free(ucs2); return krb5_enomem(context); } ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len); free(ucs2); if (ret) { free(logon_string); krb5_set_error_message(context, ret, "Failed to convert to UTF-8"); return ret; } } ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM | KRB5_PRINCIPAL_UNPARSE_DISPLAY, &principal_string); if (ret) { free(logon_string); return ret; } ret = strcmp(logon_string, principal_string); if (ret != 0) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC logon name [%s] mismatch principal name [%s]", logon_string, principal_string); } free(logon_string); free(principal_string); return ret; out: return ret; }
static krb5_error_code parse_key_set(krb5_context context, const char *key, krb5_enctype **ret_enctypes, size_t *ret_num_enctypes, krb5_salt *salt, krb5_principal principal) { const char *p; char buf[3][256]; int num_buf = 0; int i, num_enctypes = 0; krb5_enctype e; const krb5_enctype *enctypes = NULL; krb5_error_code ret; p = key; *ret_enctypes = NULL; *ret_num_enctypes = 0; /* split p in a list of :-separated strings */ for(num_buf = 0; num_buf < 3; num_buf++) if(strsep_copy(&p, ":", buf[num_buf], sizeof(buf[num_buf])) == -1) break; salt->saltvalue.data = NULL; salt->saltvalue.length = 0; for(i = 0; i < num_buf; i++) { if(enctypes == NULL && num_buf > 1) { /* this might be a etype specifier */ /* XXX there should be a string_to_etypes handling special cases like `des' and `all' */ if(strcmp(buf[i], "des") == 0) { enctypes = des_etypes; num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]); } else if(strcmp(buf[i], "des3") == 0) { e = KRB5_ENCTYPE_DES3_CBC_SHA1; enctypes = &e; num_enctypes = 1; } else { ret = krb5_string_to_enctype(context, buf[i], &e); if (ret == 0) { enctypes = &e; num_enctypes = 1; } else return ret; } continue; } if(salt->salttype == 0) { /* interpret string as a salt specifier, if no etype is set, this sets default values */ /* XXX should perhaps use string_to_salttype, but that interface sucks */ if(strcmp(buf[i], "pw-salt") == 0) { if(enctypes == NULL) { enctypes = all_etypes; num_enctypes = sizeof(all_etypes)/sizeof(all_etypes[0]); } salt->salttype = KRB5_PW_SALT; } else if(strcmp(buf[i], "afs3-salt") == 0) { if(enctypes == NULL) { enctypes = des_etypes; num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]); } salt->salttype = KRB5_AFS3_SALT; } continue; } if (salt->saltvalue.data != NULL) free(salt->saltvalue.data); /* if there is a final string, use it as the string to salt with, this is mostly useful with null salt for v4 compat, and a cell name for afs compat */ salt->saltvalue.data = strdup(buf[i]); if (salt->saltvalue.data == NULL) return krb5_enomem(context); salt->saltvalue.length = strlen(buf[i]); } if(enctypes == NULL || salt->salttype == 0) { krb5_free_salt(context, *salt); krb5_set_error_message(context, EINVAL, "bad value for default_keys `%s'", key); return EINVAL; } /* if no salt was specified make up default salt */ if(salt->saltvalue.data == NULL) { if(salt->salttype == KRB5_PW_SALT) { ret = krb5_get_pw_salt(context, principal, salt); if (ret) return ret; } else if(salt->salttype == KRB5_AFS3_SALT) { krb5_const_realm realm = krb5_principal_get_realm(context, principal); salt->saltvalue.data = strdup(realm); if(salt->saltvalue.data == NULL) { krb5_set_error_message(context, ENOMEM, "out of memory while " "parsing salt specifiers"); return ENOMEM; } strlwr(salt->saltvalue.data); salt->saltvalue.length = strlen(realm); } } *ret_enctypes = malloc(sizeof(enctypes[0]) * num_enctypes); if (*ret_enctypes == NULL) { krb5_free_salt(context, *salt); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * num_enctypes); *ret_num_enctypes = num_enctypes; return 0; }
static int do_ext_keytab(krb5_principal principal, void *data) { krb5_error_code ret; kadm5_principal_ent_rec princ; struct ext_keytab_data *e = data; krb5_keytab_entry *keys = NULL; krb5_keyblock *k = NULL; size_t i; int n_k = 0; uint32_t mask; char *unparsed = NULL; mask = KADM5_PRINCIPAL; if (!e->random_key_flag) mask |= KADM5_KVNO | KADM5_KEY_DATA; ret = kadm5_get_principal(kadm_handle, principal, &princ, mask); if (ret) return ret; ret = krb5_unparse_name(context, principal, &unparsed); if (ret) goto out; if (!e->random_key_flag) { if (princ.n_key_data == 0) { krb5_warnx(context, "principal has no keys, or user lacks " "get-keys privilege for %s", unparsed); goto out; } /* * kadmin clients and servers from master between 1.5 and 1.6 * can have corrupted a principal's keys in the HDB. If some * are bogus but not all are, then that must have happened. * * If all keys are bogus then the server may be a pre-1.6, * post-1.5 server and the client lacks get-keys privilege, or * the keys are corrupted. We can't tell here. */ if (kadm5_all_keys_are_bogus(princ.n_key_data, princ.key_data)) { krb5_warnx(context, "user lacks get-keys privilege for %s", unparsed); goto out; } if (kadm5_some_keys_are_bogus(princ.n_key_data, princ.key_data)) { krb5_warnx(context, "some keys for %s are corrupted in the HDB", unparsed); } keys = calloc(sizeof(*keys), princ.n_key_data); if (keys == NULL) { ret = krb5_enomem(context); goto out; } for (i = 0; i < princ.n_key_data; i++) { krb5_key_data *kd = &princ.key_data[i]; /* Don't extract bogus keys */ if (kadm5_all_keys_are_bogus(1, kd)) continue; keys[i].principal = princ.principal; keys[i].vno = kd->key_data_kvno; keys[i].keyblock.keytype = kd->key_data_type[0]; keys[i].keyblock.keyvalue.length = kd->key_data_length[0]; keys[i].keyblock.keyvalue.data = kd->key_data_contents[0]; keys[i].timestamp = time(NULL); n_k++; } } else if (e->random_key_flag) { ret = kadm5_randkey_principal(kadm_handle, principal, &k, &n_k); if (ret) goto out; keys = calloc(sizeof(*keys), n_k); if (keys == NULL) { ret = krb5_enomem(context); goto out; } for (i = 0; i < n_k; i++) { keys[i].principal = principal; keys[i].vno = princ.kvno + 1; /* XXX get entry again */ keys[i].keyblock = k[i]; keys[i].timestamp = time(NULL); } } if (n_k == 0) krb5_warn(context, ret, "no keys written to keytab for %s", unparsed); for (i = 0; i < n_k; i++) { ret = krb5_kt_add_entry(context, e->keytab, &keys[i]); if (ret) krb5_warn(context, ret, "krb5_kt_add_entry(%lu)", (unsigned long)i); } out: kadm5_free_principal_ent(kadm_handle, &princ); if (k) { for (i = 0; i < n_k; i++) memset(k[i].keyvalue.data, 0, k[i].keyvalue.length); free(k); } free(unparsed); free(keys); return 0; }