krb5_error_code krb5int_dk_string_to_key(const struct krb5_keytypes *ktp, const krb5_data *string, const krb5_data *salt, const krb5_data *parms, krb5_keyblock *keyblock) { krb5_error_code ret; size_t keybytes, keylength, concatlen; unsigned char *concat = NULL, *foldstring = NULL, *foldkeydata = NULL; krb5_data indata; krb5_keyblock foldkeyblock; krb5_key foldkey = NULL; /* keyblock->length is checked by krb5int_derive_key. */ keybytes = ktp->enc->keybytes; keylength = ktp->enc->keylength; concatlen = string->length + salt->length; concat = k5alloc(concatlen, &ret); if (ret != 0) goto cleanup; foldstring = k5alloc(keybytes, &ret); if (ret != 0) goto cleanup; foldkeydata = k5alloc(keylength, &ret); if (ret != 0) goto cleanup; /* construct input string ( = string + salt), fold it, make_key it */ if (string->length > 0) memcpy(concat, string->data, string->length); if (salt->length > 0) memcpy(concat + string->length, salt->data, salt->length); krb5int_nfold(concatlen*8, concat, keybytes*8, foldstring); indata.length = keybytes; indata.data = (char *) foldstring; foldkeyblock.length = keylength; foldkeyblock.contents = foldkeydata; foldkeyblock.enctype = ktp->etype; ret = ktp->rand2key(&indata, &foldkeyblock); if (ret != 0) goto cleanup; ret = krb5_k_create_key(NULL, &foldkeyblock, &foldkey); if (ret != 0) goto cleanup; /* now derive the key from this one */ indata.length = kerberos_len; indata.data = (char *) kerberos; ret = krb5int_derive_keyblock(ktp->enc, NULL, foldkey, keyblock, &indata, DERIVE_RFC3961); if (ret != 0) memset(keyblock->contents, 0, keyblock->length); cleanup: zapfree(concat, concatlen); zapfree(foldstring, keybytes); zapfree(foldkeydata, keylength); krb5_k_free_key(NULL, foldkey); return ret; }
/* Used by the slave to update its hash db from* the incr update log. Must be * called with lock held. */ krb5_error_code ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args) { krb5_db_entry *entry = NULL; kdb_incr_update_t *upd = NULL, *fupd; int i, no_of_updates; krb5_error_code retval; krb5_principal dbprinc; kdb_last_t errlast; char *dbprincstr; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; INIT_ULOG(context); no_of_updates = incr_ret->updates.kdb_ulog_t_len; upd = incr_ret->updates.kdb_ulog_t_val; fupd = upd; /* We reset last_sno and last_time to 0, if krb5_db2_db_put_principal or * krb5_db2_db_delete_principal fail. */ errlast.last_sno = (unsigned int)0; errlast.last_time.seconds = (unsigned int)0; errlast.last_time.useconds = (unsigned int)0; retval = krb5_db_open(context, db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); if (retval) goto cleanup; for (i = 0; i < no_of_updates; i++) { if (!upd->kdb_commit) continue; if (upd->kdb_deleted) { dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val, upd->kdb_princ_name.utf8str_t_len, &retval); if (dbprincstr == NULL) goto cleanup; retval = krb5_parse_name(context, dbprincstr, &dbprinc); free(dbprincstr); if (retval) goto cleanup; retval = krb5int_delete_principal_no_log(context, dbprinc); krb5_free_principal(context, dbprinc); if (retval) goto cleanup; } else { entry = k5alloc(sizeof(krb5_db_entry), &retval); if (entry == NULL) goto cleanup; retval = ulog_conv_2dbentry(context, &entry, upd); if (retval) goto cleanup; retval = krb5int_put_principal_no_log(context, entry); krb5_db_free_principal(context, entry); if (retval) goto cleanup; } upd++; } cleanup: if (fupd) ulog_free_entries(fupd, no_of_updates); if (retval) ulog_finish_update_slave(ulog, errlast); else ulog_finish_update_slave(ulog, incr_ret->lastentry); return retval; }
/* * Map the log file to memory for performance and simplicity. * * Called by: if iprop_enabled then ulog_map(); * Assumes that the caller will terminate on ulog_map, hence munmap and * closing of the fd are implicitly performed by the caller. * * Semantics for various values of caller: * * - FKPROPLOG * * Don't create if it doesn't exist, map as MAP_PRIVATE. * * - FKPROPD * * Create and initialize if need be, map as MAP_SHARED. * * - FKLOAD * * Create if need be, initialize (even if the ulog was already present), map * as MAP_SHARED. (Intended for kdb5_util load of iprop dump.) * * - FKCOMMAND * * Create and [re-]initialize if need be, size appropriately, map as * MAP_SHARED. (Intended for kdb5_util create and kdb5_util load of * non-iprop dump.) * * - FKADMIN * * Create and [re-]initialize if need be, size appropriately, map as * MAP_SHARED, and check consistency and recover as necessary. (Intended * for kadmind and kadmin.local.) * * Returns 0 on success else failure. */ krb5_error_code ulog_map(krb5_context context, const char *logname, uint32_t ulogentries, int caller, char **db_args) { struct stat st; krb5_error_code retval; uint32_t ulog_filesize; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; int ulogfd = -1; ulog_filesize = sizeof(kdb_hlog_t); if (stat(logname, &st) == -1) { /* File doesn't exist so we exit with kproplog. */ if (caller == FKPROPLOG) return errno; ulogfd = open(logname, O_RDWR | O_CREAT, 0600); if (ulogfd == -1) return errno; if (lseek(ulogfd, 0L, SEEK_CUR) == -1) return errno; if (caller == FKADMIND || caller == FKCOMMAND) ulog_filesize += ulogentries * ULOG_BLOCK; if (extend_file_to(ulogfd, ulog_filesize) < 0) return errno; } else { ulogfd = open(logname, O_RDWR, 0600); if (ulogfd == -1) return errno; } if (caller == FKPROPLOG) { if (fstat(ulogfd, &st) < 0) { close(ulogfd); return errno; } ulog_filesize = st.st_size; ulog = mmap(0, ulog_filesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, ulogfd, 0); } else { /* kadmind, kpropd, & kcommands should udpate stores. */ ulog = mmap(0, MAXLOGLEN, PROT_READ | PROT_WRITE, MAP_SHARED, ulogfd, 0); } if (ulog == MAP_FAILED) { /* Can't map update log file to memory. */ close(ulogfd); return errno; } if (!context->kdblog_context) { log_ctx = k5alloc(sizeof(kdb_log_context), &retval); if (log_ctx == NULL) return retval; memset(log_ctx, 0, sizeof(*log_ctx)); context->kdblog_context = log_ctx; } else { log_ctx = context->kdblog_context; } log_ctx->ulog = ulog; log_ctx->ulogentries = ulogentries; log_ctx->ulogfd = ulogfd; retval = ulog_lock(context, KRB5_LOCKMODE_EXCLUSIVE); if (retval) return retval; if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC && ulog->kdb_hmagic != 0) { ulog_lock(context, KRB5_LOCKMODE_UNLOCK); return KRB5_LOG_CORRUPT; } if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC || caller == FKLOAD) { ulog_reset(ulog); if (caller != FKPROPLOG) ulog_sync_header(ulog); ulog_lock(context, KRB5_LOCKMODE_UNLOCK); return 0; } if (caller == FKPROPLOG || caller == FKPROPD) { /* kproplog and kpropd don't need to do anything else. */ ulog_lock(context, KRB5_LOCKMODE_UNLOCK); return 0; } assert(caller == FKADMIND || caller == FKCOMMAND); /* Reinit ulog if the log is being truncated or expanded after we have * circled. */ if (ulog->kdb_num != ulogentries) { if (ulog->kdb_num != 0 && (ulog->kdb_last_sno > ulog->kdb_num || ulog->kdb_num > ulogentries)) { ulog_reset(ulog); ulog_sync_header(ulog); } /* Expand ulog if we have specified a greater size. */ if (ulog->kdb_num < ulogentries) { ulog_filesize += ulogentries * ulog->kdb_block; if (extend_file_to(ulogfd, ulog_filesize) < 0) { ulog_lock(context, KRB5_LOCKMODE_UNLOCK); return errno; } } } ulog_lock(context, KRB5_LOCKMODE_UNLOCK); return 0; }
krb5_error_code kh_unmarshal_hdb_entry(krb5_context context, const hdb_entry *hentry, krb5_db_entry **kentry_ptr) { kh_db_context *kh = KH_DB_CONTEXT(context); krb5_db_entry *kentry; krb5_error_code code; unsigned int i; kentry = k5alloc(sizeof(*kentry), &code); if (kentry == NULL) return code; kentry->magic = KRB5_KDB_MAGIC_NUMBER; kentry->len = KRB5_KDB_V1_BASE_LENGTH; code = kh_unmarshal_Principal(context, hentry->principal, &kentry->princ); if (code != 0) goto cleanup; code = kh_unmarshal_HDBFlags(context, hentry->flags, &kentry->attributes); if (code != 0) goto cleanup; if (hentry->max_life != NULL) kentry->max_life = *(hentry->max_life); if (hentry->max_renew != NULL) kentry->max_renewable_life = *(hentry->max_renew); if (hentry->valid_end != NULL) kentry->expiration = *(hentry->valid_end); if (hentry->pw_end != NULL) kentry->pw_expiration = *(hentry->pw_end); /* last_success */ /* last_failed */ /* fail_auth_count */ /* n_tl_data */ code = kh_unmarshal_Event(context, hentry->modified_by ? hentry->modified_by : &hentry->created_by, kentry); if (code != 0) goto cleanup; code = kh_unmarshal_HDB_extensions(context, hentry->extensions, kentry); if (code != 0) goto cleanup; kentry->key_data = k5alloc(hentry->keys.len * sizeof(krb5_key_data), &code); if (code != 0) goto cleanup; for (i = 0; i < hentry->keys.len; i++) { code = kh_unmarshal_Key(context, hentry, &hentry->keys.val[i], &kentry->key_data[i]); if (code != 0) goto cleanup; kentry->n_key_data++; } *kentry_ptr = kentry; kentry = NULL; cleanup: kh_kdb_free_entry(context, kh, kentry); return code; }
krb5_error_code krb5_db2_promote_db(krb5_context context, char *conf_section, char **db_args) { krb5_error_code retval; krb5_boolean merge_nra = FALSE, real_locked = FALSE; krb5_db2_context *dbc_temp, *dbc_real = NULL; char **db_argp; /* context must be initialized with an exclusively locked temp DB. */ if (!inited(context)) return KRB5_KDB_DBNOTINITED; dbc_temp = context->dal_handle->db_context; if (dbc_temp->db_lock_mode != KRB5_LOCKMODE_EXCLUSIVE) return KRB5_KDB_NOTLOCKED; if (!dbc_temp->tempdb) return EINVAL; /* Check db_args for whether we should merge non-replicated attributes. */ for (db_argp = db_args; *db_argp; db_argp++) { if (!strcmp(*db_argp, "merge_nra")) { merge_nra = TRUE; break; } } /* Make a db2 context for the real DB. */ dbc_real = k5alloc(sizeof(*dbc_real), &retval); if (dbc_real == NULL) return retval; ctx_clear(dbc_real); /* Try creating the real DB. */ dbc_real->db_name = strdup(dbc_temp->db_name); if (dbc_real->db_name == NULL) goto cleanup; dbc_real->tempdb = FALSE; retval = ctx_create_db(context, dbc_real); if (retval == EEXIST) { /* The real database already exists, so open and lock it. */ dbc_real->db_name = strdup(dbc_temp->db_name); if (dbc_real->db_name == NULL) goto cleanup; dbc_real->tempdb = FALSE; retval = ctx_init(dbc_real); if (retval) goto cleanup; retval = ctx_lock(context, dbc_real, KRB5_DB_LOCKMODE_EXCLUSIVE); if (retval) goto cleanup; } else if (retval) goto cleanup; real_locked = TRUE; if (merge_nra) { retval = ctx_merge_nra(context, dbc_temp, dbc_real); if (retval) goto cleanup; } /* Perform filesystem manipulations for the promotion. */ retval = ctx_promote(context, dbc_temp, dbc_real); if (retval) goto cleanup; /* Unlock and finalize context since the temp DB is gone. */ (void) krb5_db2_unlock(context); krb5_db2_fini(context); cleanup: if (real_locked) (void) ctx_unlock(context, dbc_real); if (dbc_real) ctx_fini(dbc_real); return retval; }
static krb5_error_code test_process(krb5_context context, krb5_clpreauth_moddata moddata, krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *pa_data, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***out_pa_data) { struct client_state *st = (struct client_state *)moddata; struct client_request_state *reqst = (struct client_request_state *)modreq; krb5_error_code ret; krb5_pa_data **list, *pa; krb5_keyblock *k; krb5_enc_data enc; krb5_data plain; const char *indstr; if (pa_data->length == 0) { /* This is an optimistic preauth test. Send a recognizable padata * value so the KDC knows not to expect a cookie. */ list = k5calloc(2, sizeof(*list), &ret); assert(!ret); pa = k5alloc(sizeof(*pa), &ret); assert(!ret); pa->pa_type = TEST_PA_TYPE; pa->contents = (uint8_t *)strdup("optimistic"); assert(pa->contents != NULL); pa->length = 10; list[0] = pa; list[1] = NULL; *out_pa_data = list; return 0; } else if (reqst->second_round_trip) { printf("2rt: %.*s\n", pa_data->length, pa_data->contents); } else if (pa_data->length == 6 && memcmp(pa_data->contents, "no key", 6) == 0) { printf("no key\n"); } else { /* This fails during s4u_identify_user(), so don't assert. */ ret = cb->get_as_key(context, rock, &k); if (ret) return ret; ret = alloc_data(&plain, pa_data->length); assert(!ret); enc.enctype = k->enctype; enc.ciphertext = make_data(pa_data->contents, pa_data->length); ret = krb5_c_decrypt(context, k, 1024, NULL, &enc, &plain); assert(!ret); printf("%.*s\n", plain.length, plain.data); free(plain.data); } reqst->second_round_trip = TRUE; indstr = (st->indicators != NULL) ? st->indicators : ""; list = k5calloc(2, sizeof(*list), &ret); assert(!ret); pa = k5alloc(sizeof(*pa), &ret); assert(!ret); pa->pa_type = TEST_PA_TYPE; pa->contents = (uint8_t *)strdup(indstr); assert(pa->contents != NULL); pa->length = strlen(indstr); list[0] = pa; list[1] = NULL; *out_pa_data = list; return 0; }
krb5_error_code krb5int_hmac_keyblock(const struct krb5_hash_provider *hash, const krb5_keyblock *keyblock, const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { unsigned char *xorkey = NULL, *ihash = NULL; unsigned int i; krb5_crypto_iov *ihash_iov = NULL, ohash_iov[2]; krb5_data hashout; krb5_error_code ret; if (keyblock->length > hash->blocksize) return KRB5_CRYPTO_INTERNAL; if (output->length < hash->hashsize) return KRB5_BAD_MSIZE; /* Allocate space for the xor key, hash input vector, and inner hash */ xorkey = k5alloc(hash->blocksize, &ret); if (xorkey == NULL) goto cleanup; ihash = k5alloc(hash->hashsize, &ret); if (ihash == NULL) goto cleanup; ihash_iov = k5calloc(num_data + 1, sizeof(krb5_crypto_iov), &ret); if (ihash_iov == NULL) goto cleanup; /* Create the inner padded key. */ memset(xorkey, 0x36, hash->blocksize); for (i = 0; i < keyblock->length; i++) xorkey[i] ^= keyblock->contents[i]; /* Compute the inner hash over the inner key and input data. */ ihash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA; ihash_iov[0].data = make_data(xorkey, hash->blocksize); memcpy(ihash_iov + 1, data, num_data * sizeof(krb5_crypto_iov)); hashout = make_data(ihash, hash->hashsize); ret = hash->hash(ihash_iov, num_data + 1, &hashout); if (ret != 0) goto cleanup; /* Create the outer padded key. */ memset(xorkey, 0x5c, hash->blocksize); for (i = 0; i < keyblock->length; i++) xorkey[i] ^= keyblock->contents[i]; /* Compute the outer hash over the outer key and inner hash value. */ ohash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA; ohash_iov[0].data = make_data(xorkey, hash->blocksize); ohash_iov[1].flags = KRB5_CRYPTO_TYPE_DATA; ohash_iov[1].data = make_data(ihash, hash->hashsize); output->length = hash->hashsize; ret = hash->hash(ohash_iov, 2, output); if (ret != 0) memset(output->data, 0, output->length); cleanup: zapfree(xorkey, hash->blocksize); zapfree(ihash, hash->hashsize); free(ihash_iov); return ret; }
static krb5_error_code prepare_error_as(struct kdc_request_state *rstate, krb5_kdc_req *request, krb5_db_entry *local_tgt, int error, krb5_pa_data **e_data_in, krb5_boolean typed_e_data, krb5_principal canon_client, krb5_data **response, const char *status) { krb5_error errpkt; krb5_error_code retval; krb5_data *scratch = NULL, *e_data_asn1 = NULL, *fast_edata = NULL; krb5_pa_data **e_data = NULL, *cookie = NULL; kdc_realm_t *kdc_active_realm = rstate->realm_data; size_t count; if (e_data_in != NULL) { /* Add a PA-FX-COOKIE to e_data_in. e_data is a shallow copy * containing aliases. */ for (count = 0; e_data_in[count] != NULL; count++); e_data = calloc(count + 2, sizeof(*e_data)); if (e_data == NULL) return ENOMEM; memcpy(e_data, e_data_in, count * sizeof(*e_data)); retval = kdc_fast_make_cookie(kdc_context, rstate, local_tgt, request->client, &cookie); e_data[count] = cookie; } errpkt.ctime = request->nonce; errpkt.cusec = 0; retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec); if (retval) goto cleanup; errpkt.error = error; errpkt.server = request->server; errpkt.client = (error == KDC_ERR_WRONG_REALM) ? canon_client : request->client; errpkt.text = string2data((char *)status); if (e_data != NULL) { if (typed_e_data) retval = encode_krb5_typed_data(e_data, &e_data_asn1); else retval = encode_krb5_padata_sequence(e_data, &e_data_asn1); if (retval) goto cleanup; errpkt.e_data = *e_data_asn1; } else errpkt.e_data = empty_data(); retval = kdc_fast_handle_error(kdc_context, rstate, request, e_data, &errpkt, &fast_edata); if (retval) goto cleanup; if (fast_edata != NULL) errpkt.e_data = *fast_edata; scratch = k5alloc(sizeof(*scratch), &retval); if (scratch == NULL) goto cleanup; if (kdc_fast_hide_client(rstate) && errpkt.client != NULL) errpkt.client = (krb5_principal)krb5_anonymous_principal(); retval = krb5_mk_error(kdc_context, &errpkt, scratch); if (retval) goto cleanup; *response = scratch; scratch = NULL; cleanup: krb5_free_data(kdc_context, fast_edata); krb5_free_data(kdc_context, e_data_asn1); free(scratch); free(e_data); if (cookie != NULL) free(cookie->contents); free(cookie); return retval; }
krb5_error_code krb5int_c_combine_keys(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, krb5_keyblock *outkey) { unsigned char *r1 = NULL, *r2 = NULL, *combined = NULL, *rnd = NULL; unsigned char *output = NULL; size_t keybytes, keylength; const struct krb5_enc_provider *enc; krb5_data input, randbits; krb5_keyblock tkeyblock; krb5_key tkey = NULL; krb5_error_code ret; const struct krb5_keytypes *ktp; krb5_boolean myalloc = FALSE; if (!enctype_ok(key1->enctype) || !enctype_ok(key2->enctype)) return KRB5_CRYPTO_INTERNAL; if (key1->length != key2->length || key1->enctype != key2->enctype) return KRB5_CRYPTO_INTERNAL; /* Find our encryption algorithm. */ ktp = find_enctype(key1->enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; enc = ktp->enc; keybytes = enc->keybytes; keylength = enc->keylength; /* Allocate and set up buffers. */ r1 = k5alloc(keybytes, &ret); if (ret) goto cleanup; r2 = k5alloc(keybytes, &ret); if (ret) goto cleanup; rnd = k5alloc(keybytes, &ret); if (ret) goto cleanup; combined = k5alloc(keybytes * 2, &ret); if (ret) goto cleanup; output = k5alloc(keylength, &ret); if (ret) goto cleanup; /* * Get R1 and R2 (by running the input keys through the DR algorithm. * Note this is most of derive-key, but not all. */ input.length = key2->length; input.data = (char *) key2->contents; ret = dr(enc, key1, r1, &input); if (ret) goto cleanup; input.length = key1->length; input.data = (char *) key1->contents; ret = dr(enc, key2, r2, &input); if (ret) goto cleanup; /* * Concatenate the two keys together, and then run them through * n-fold to reduce them to a length appropriate for the random-to-key * operation. Note here that krb5int_nfold() takes sizes in bits, hence * the multiply by 8. */ memcpy(combined, r1, keybytes); memcpy(combined + keybytes, r2, keybytes); krb5int_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd); /* * Run the "random" bits through random-to-key to produce a encryption * key. */ randbits.length = keybytes; randbits.data = (char *) rnd; tkeyblock.length = keylength; tkeyblock.contents = output; ret = (*ktp->rand2key)(&randbits, &tkeyblock); if (ret) goto cleanup; ret = krb5_k_create_key(NULL, &tkeyblock, &tkey); if (ret) goto cleanup; /* * Run through derive-key one more time to produce the final key. * Note that the input to derive-key is the ASCII string "combine". */ input.length = 7; input.data = "combine"; /* * Just FYI: _if_ we have space here in the key, then simply use it * without modification. But if the key is blank (no allocated storage) * then allocate some memory for it. This allows programs to use one of * the existing keys as the output key, _or_ pass in a blank keyblock * for us to allocate. It's easier for us to allocate it since we already * know the crypto library internals */ if (outkey->length == 0 || outkey->contents == NULL) { outkey->contents = k5alloc(keylength, &ret); if (ret) goto cleanup; outkey->length = keylength; outkey->enctype = key1->enctype; myalloc = TRUE; } ret = krb5int_derive_keyblock(enc, tkey, outkey, &input, DERIVE_RFC3961); if (ret) { if (myalloc) { free(outkey->contents); outkey->contents = NULL; } goto cleanup; } cleanup: zapfree(r1, keybytes); zapfree(r2, keybytes); zapfree(rnd, keybytes); zapfree(combined, keybytes * 2); zapfree(output, keylength); krb5_k_free_key(NULL, tkey); return ret; }
krb5_error_code krb5_ldap_get_principal(krb5_context context, krb5_const_principal searchfor, unsigned int flags, krb5_db_entry **entry_ptr) { char *user=NULL, *filter=NULL, *filtuser=NULL; unsigned int tree=0, ntrees=1, princlen=0; krb5_error_code tempst=0, st=0; char **values=NULL, **subtree=NULL, *cname=NULL; LDAP *ld=NULL; LDAPMessage *result=NULL, *ent=NULL; krb5_ldap_context *ldap_context=NULL; kdb5_dal_handle *dal_handle=NULL; krb5_ldap_server_handle *ldap_server_handle=NULL; krb5_principal cprinc=NULL; krb5_boolean found=FALSE; krb5_db_entry *entry = NULL; *entry_ptr = NULL; /* Clear the global error string */ krb5_clear_error_message(context); if (searchfor == NULL) return EINVAL; dal_handle = context->dal_handle; ldap_context = (krb5_ldap_context *) dal_handle->db_context; CHECK_LDAP_HANDLE(ldap_context); if (is_principal_in_realm(ldap_context, searchfor) != 0) { st = KRB5_KDB_NOENTRY; krb5_set_error_message(context, st, _("Principal does not belong to realm")); goto cleanup; } if ((st=krb5_unparse_name(context, searchfor, &user)) != 0) goto cleanup; if ((st=krb5_ldap_unparse_principal_name(user)) != 0) goto cleanup; filtuser = ldap_filter_correct(user); if (filtuser == NULL) { st = ENOMEM; goto cleanup; } princlen = strlen(FILTER) + strlen(filtuser) + 2 + 1; /* 2 for closing brackets */ if ((filter = malloc(princlen)) == NULL) { st = ENOMEM; goto cleanup; } snprintf(filter, princlen, FILTER"%s))", filtuser); if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0) goto cleanup; GET_HANDLE(); for (tree=0; tree < ntrees && !found; ++tree) { LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes); for (ent=ldap_first_entry(ld, result); ent != NULL && !found; ent=ldap_next_entry(ld, ent)) { /* get the associated directory user information */ if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) { int i; /* a wild-card in a principal name can return a list of kerberos principals. * Make sure that the correct principal is returned. * NOTE: a principalname k* in ldap server will return all the principals starting with a k */ for (i=0; values[i] != NULL; ++i) { if (strcmp(values[i], user) == 0) { found = TRUE; break; } } ldap_value_free(values); if (!found) /* no matching principal found */ continue; } if ((values=ldap_get_values(ld, ent, "krbcanonicalname")) != NULL) { if (values[0] && strcmp(values[0], user) != 0) { /* We matched an alias, not the canonical name. */ if (flags & KRB5_KDB_FLAG_ALIAS_OK) { st = krb5_ldap_parse_principal_name(values[0], &cname); if (st != 0) goto cleanup; st = krb5_parse_name(context, cname, &cprinc); if (st != 0) goto cleanup; } else /* No canonicalization, so don't return aliases. */ found = FALSE; } ldap_value_free(values); if (!found) continue; } entry = k5alloc(sizeof(*entry), &st); if (entry == NULL) goto cleanup; if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, cprinc ? cprinc : searchfor, entry)) != 0) goto cleanup; } ldap_msgfree(result); result = NULL; } /* for (tree=0 ... */ if (found) { *entry_ptr = entry; entry = NULL; } else st = KRB5_KDB_NOENTRY; cleanup: ldap_msgfree(result); krb5_ldap_free_principal(context, entry); if (filter) free (filter); if (subtree) { for (; ntrees; --ntrees) if (subtree[ntrees-1]) free (subtree[ntrees-1]); free (subtree); } if (ldap_server_handle) krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); if (user) free(user); if (filtuser) free(filtuser); if (cname) free(cname); if (cprinc) krb5_free_principal(context, cprinc); return st; }
/* * Scan name and allocate a shell principal with enough space in each field. * If enterprise is true, use enterprise principal parsing rules. Return * KRB5_PARSE_MALFORMED if name is malformed. Set *has_realm_out according to * whether name contains a realm separator. */ static krb5_error_code allocate_princ(krb5_context context, const char *name, krb5_boolean enterprise, krb5_principal *princ_out, krb5_boolean *has_realm_out) { krb5_error_code ret; const char *p; krb5_boolean first_at = TRUE; krb5_principal princ = NULL; krb5_data *cur_data, *new_comps; krb5_int32 i; *princ_out = NULL; *has_realm_out = FALSE; /* Allocate a starting principal with one component. */ princ = k5alloc(sizeof(*princ), &ret); if (princ == NULL) goto cleanup; princ->data = k5alloc(sizeof(*princ->data), &ret); if (princ->data == NULL) goto cleanup; princ->realm = empty_data(); princ->data[0] = empty_data(); princ->length = 1; cur_data = &princ->data[0]; for (p = name; *p != '\0'; p++) { if (*p == '/' && !enterprise) { /* Component separator (for non-enterprise principals). We * shouldn't see this in the realm name. */ if (cur_data == &princ->realm) { ret = KRB5_PARSE_MALFORMED; goto cleanup; } new_comps = realloc(princ->data, (princ->length + 1) * sizeof(*princ->data)); if (new_comps == NULL) { ret = ENOMEM; goto cleanup; } princ->data = new_comps; princ->length++; cur_data = &princ->data[princ->length - 1]; *cur_data = empty_data(); } else if (*p == '@' && (!enterprise || !first_at)) { /* Realm separator. In enterprise principals, the first one of * these we see is part of the component. */ cur_data = &princ->realm; } else { /* Component or realm character, possibly quoted. Make note if * we're seeing the first '@' in an enterprise principal. */ cur_data->length++; if (*p == '@' && enterprise) first_at = FALSE; if (*p == '\\') { /* Quote character can't be the last character of the name. */ if (*++p == '\0') { ret = KRB5_PARSE_MALFORMED; goto cleanup; } } } } /* Allocate space for each component and the realm, with space for null * terminators on each field. */ for (i = 0; i < princ->length; i++) { princ->data[i].data = k5alloc(princ->data[i].length + 1, &ret); if (princ->data[i].data == NULL) goto cleanup; } princ->realm.data = k5alloc(princ->realm.length + 1, &ret); if (princ->realm.data == NULL) goto cleanup; *princ_out = princ; *has_realm_out = (cur_data == &princ->realm); princ = NULL; cleanup: krb5_free_principal(context, princ); return ret; }
/* * Fill out a krb5_db_entry princ entry struct given a LDAP message containing * the results of a principal search of the directory. */ krb5_error_code populate_krb5_db_entry(krb5_context context, krb5_ldap_context *ldap_context, LDAP *ld, LDAPMessage *ent, krb5_const_principal princ, krb5_db_entry *entry) { krb5_error_code ret; unsigned int mask = 0; int val, i, pcount, objtype; krb5_boolean attr_present; krb5_kvno mkvno = 0; krb5_timestamp lastpwdchange, unlock_time; char *policydn = NULL, *pwdpolicydn = NULL, *polname = NULL, *user = NULL; char *tktpolname = NULL, *dn = NULL, **link_references = NULL; char **pnvalues = NULL, **ocvalues = NULL, **a2d2 = NULL; struct berval **ber_key_data = NULL, **ber_tl_data = NULL; krb5_tl_data userinfo_tl_data = { NULL }, **endp, *tl; ret = krb5_copy_principal(context, princ, &entry->princ); if (ret) goto cleanup; /* get the associated directory user information */ pnvalues = ldap_get_values(ld, ent, "krbprincipalname"); if (pnvalues != NULL) { ret = krb5_unparse_name(context, princ, &user); if (ret) goto cleanup; pcount = 0; for (i = 0; pnvalues[i] != NULL; i++) { if (strcasecmp(pnvalues[i], user) == 0) { pcount = ldap_count_values(pnvalues); break; } } dn = ldap_get_dn(ld, ent); if (dn == NULL) { ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &ret); ret = set_ldap_error(context, ret, 0); goto cleanup; } ocvalues = ldap_get_values(ld, ent, "objectclass"); if (ocvalues != NULL) { for (i = 0; ocvalues[i] != NULL; i++) { if (strcasecmp(ocvalues[i], "krbprincipal") == 0) { objtype = KDB_STANDALONE_PRINCIPAL_OBJECT; ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE, &objtype); if (ret) goto cleanup; break; } } } /* Add principalcount, DN and principaltype user information to * tl_data */ ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount); if (ret) goto cleanup; ret = store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, dn); if (ret) goto cleanup; } ret = get_time(ld, ent, "krbLastSuccessfulAuth", &entry->last_success, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_LAST_SUCCESS_ATTR; ret = get_time(ld, ent, "krbLastFailedAuth", &entry->last_failed, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_LAST_FAILED_ATTR; if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount", &val) == 0) { entry->fail_auth_count = val; mask |= KDB_FAIL_AUTH_COUNT_ATTR; } if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &entry->max_life) == 0) mask |= KDB_MAX_LIFE_ATTR; if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", &entry->max_renewable_life) == 0) mask |= KDB_MAX_RLIFE_ATTR; if (krb5_ldap_get_value(ld, ent, "krbticketflags", &entry->attributes) == 0) mask |= KDB_TKT_FLAGS_ATTR; ret = get_time(ld, ent, "krbprincipalexpiration", &entry->expiration, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_PRINC_EXPIRE_TIME_ATTR; ret = get_time(ld, ent, "krbpasswordexpiration", &entry->pw_expiration, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_PWD_EXPIRE_TIME_ATTR; ret = krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn, &attr_present); if (ret) goto cleanup; if (attr_present) { mask |= KDB_POL_REF_ATTR; /* Ensure that the policy is inside the realm container. */ ret = krb5_ldap_policydn_to_name(context, policydn, &tktpolname); if (ret) goto cleanup; } ret = krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn, &attr_present); if (ret) goto cleanup; if (attr_present) { mask |= KDB_PWD_POL_REF_ATTR; /* Ensure that the policy is inside the realm container. */ ret = krb5_ldap_policydn_to_name(context, pwdpolicydn, &polname); if (ret) goto cleanup; ret = krb5_update_tl_kadm_data(context, entry, polname); if (ret) goto cleanup; } ber_key_data = ldap_get_values_len(ld, ent, "krbprincipalkey"); if (ber_key_data != NULL) { mask |= KDB_SECRET_KEY_ATTR; ret = krb5_decode_krbsecretkey(context, entry, ber_key_data, &userinfo_tl_data, &mkvno); if (ret) goto cleanup; if (mkvno != 0) { ret = krb5_dbe_update_mkvno(context, entry, mkvno); if (ret) goto cleanup; } } ret = get_time(ld, ent, "krbLastPwdChange", &lastpwdchange, &attr_present); if (ret) goto cleanup; if (attr_present) { ret = krb5_dbe_update_last_pwd_change(context, entry, lastpwdchange); if (ret) goto cleanup; mask |= KDB_LAST_PWD_CHANGE_ATTR; } ret = get_time(ld, ent, "krbLastAdminUnlock", &unlock_time, &attr_present); if (ret) goto cleanup; if (attr_present) { ret = krb5_dbe_update_last_admin_unlock(context, entry, unlock_time); if (ret) goto cleanup; mask |= KDB_LAST_ADMIN_UNLOCK_ATTR; } a2d2 = ldap_get_values(ld, ent, "krbAllowedToDelegateTo"); if (a2d2 != NULL) { for (endp = &entry->tl_data; *endp; endp = &(*endp)->tl_data_next); for (i = 0; a2d2[i] != NULL; i++) { tl = k5alloc(sizeof(*tl), &ret); if (tl == NULL) goto cleanup; tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL; tl->tl_data_length = strlen(a2d2[i]); tl->tl_data_contents = (unsigned char *)strdup(a2d2[i]); if (tl->tl_data_contents == NULL) { ret = ENOMEM; free(tl); goto cleanup; } tl->tl_data_next = NULL; *endp = tl; endp = &tl->tl_data_next; } } link_references = ldap_get_values(ld, ent, "krbobjectreferences"); if (link_references != NULL) { for (i = 0; link_references[i] != NULL; i++) { ret = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN, link_references[i]); if (ret) goto cleanup; } } ber_tl_data = ldap_get_values_len(ld, ent, "krbExtraData"); if (ber_tl_data != NULL) { for (i = 0; ber_tl_data[i] != NULL; i++) { ret = berval2tl_data(ber_tl_data[i], &tl); if (ret) goto cleanup; ret = krb5_dbe_update_tl_data(context, entry, tl); free(tl->tl_data_contents); free(tl); if (ret) goto cleanup; } mask |= KDB_EXTRA_DATA_ATTR; } /* Update the mask of attributes present on the directory object to the * tl_data. */ ret = store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask); if (ret) goto cleanup; ret = krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data); if (ret) goto cleanup; ret = krb5_read_tkt_policy(context, ldap_context, entry, tktpolname); if (ret) goto cleanup; /* For compatibility with DB2 principals. */ entry->len = KRB5_KDB_V1_BASE_LENGTH; cleanup: ldap_memfree(dn); ldap_value_free_len(ber_key_data); ldap_value_free_len(ber_tl_data); ldap_value_free(pnvalues); ldap_value_free(ocvalues); ldap_value_free(link_references); ldap_value_free(a2d2); free(userinfo_tl_data.tl_data_contents); free(pwdpolicydn); free(polname); free(tktpolname); free(policydn); krb5_free_unparsed_name(context, user); return ret; }
krb5_error_code verify_securid_data_2(krb5_context context, krb5_db_entry *client, krb5_sam_response_2 *sr2, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa, krb5_sam_challenge_2 **sc2_out) { krb5_error_code retval; int new_pin = 0; krb5_key_data *client_key_data = NULL; krb5_keyblock client_key; krb5_data scratch; krb5_enc_sam_response_enc_2 *esre2 = NULL; struct securid_track_data sid_track_data, *trackp = NULL; krb5_data tmp_data; SDI_HANDLE sd_handle = SDI_HANDLE_NONE; krb5_sam_challenge_2 *sc2p = NULL; char *cp, *user = NULL; char *securid_user = NULL; char passcode[LENPRNST+1]; char max_pin_len, min_pin_len, alpha_pin; memset(&client_key, 0, sizeof(client_key)); memset(&scratch, 0, sizeof(scratch)); *sc2_out = NULL; retval = krb5_unparse_name(context, client->princ, &user); if (retval != 0) { com_err("krb5kdc", retval, "while unparsing client name in verify_securid_data_2"); return retval; } if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) || (sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; k5_setmsg(context, retval, "No preauth data supplied in verify_securid_data_2 (%s)", user); goto cleanup; } retval = krb5_dbe_find_enctype(context, client, sr2->sam_enc_nonce_or_sad.enctype, -1, sr2->sam_enc_nonce_or_sad.kvno, &client_key_data); if (retval) { com_err("krb5kdc", retval, "while getting client key in verify_securid_data_2 (%s)", user); goto cleanup; } retval = krb5_dbe_decrypt_key_data(context, NULL, client_key_data, &client_key, NULL); if (retval != 0) { com_err("krb5kdc", retval, "while decrypting client key in verify_securid_data_2 (%s)", user); goto cleanup; } scratch.length = sr2->sam_enc_nonce_or_sad.ciphertext.length; scratch.data = k5alloc(scratch.length, &retval); if (retval) goto cleanup; retval = krb5_c_decrypt(context, &client_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, 0, &sr2->sam_enc_nonce_or_sad, &scratch); if (retval) { com_err("krb5kdc", retval, "while decrypting SAD in verify_securid_data_2 (%s)", user); goto cleanup; } retval = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2); if (retval) { com_err("krb5kdc", retval, "while decoding SAD in verify_securid_data_2 (%s)", user); esre2 = NULL; goto cleanup; } if (sr2->sam_nonce != esre2->sam_nonce) { com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "while checking nonce in verify_securid_data_2 (%s)", user); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) { com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "No SecurID passcode in verify_securid_data_2 (%s)", user); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } /* Copy out SAD to null-terminated buffer */ memset(passcode, 0, sizeof(passcode)); if (esre2->sam_sad.length > (sizeof(passcode) - 1)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; com_err("krb5kdc", retval, "SecurID passcode/PIN too long (%d bytes) in " "verify_securid_data_2 (%s)", esre2->sam_sad.length, user); goto cleanup; } if (esre2->sam_sad.length > 0) memcpy(passcode, esre2->sam_sad.data, esre2->sam_sad.length); securid_user = strdup(user); if (!securid_user) { retval = ENOMEM; com_err("krb5kdc", ENOMEM, "while copying user name in verify_securid_data_2 (%s)", user); goto cleanup; } cp = strchr(securid_user, '@'); if (cp != NULL) *cp = '\0'; /* Check for any track_id data that may have state from a previous attempt * at SecurID authentication. */ if (sr2->sam_track_id.data && (sr2->sam_track_id.length > 0)) { krb5_data track_id_data; memset(&track_id_data, 0, sizeof(track_id_data)); retval = securid_decrypt_track_data_2(context, client, &sr2->sam_track_id, &track_id_data); if (retval) { com_err("krb5kdc", retval, "while decrypting SecurID trackID in " "verify_securid_data_2 (%s)", user); goto cleanup; } if (track_id_data.length < sizeof (struct securid_track_data)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; com_err("krb5kdc", retval, "Length of track data incorrect"); goto cleanup; } trackp = (struct securid_track_data *)track_id_data.data; if(trackp->hostid != gethostid()) { krb5_klog_syslog(LOG_INFO, "Unexpected challenge response"); retval = KRB5KDC_ERR_DISCARD; goto cleanup; } switch(trackp->state) { case SECURID_STATE_INITIAL: goto initial; break; case SECURID_STATE_NEW_PIN_AGAIN: { int pin1_len, pin2_len; trackp->handle = ntohl(trackp->handle); pin2_len = strlen(passcode); pin1_len = strlen(trackp->passcode); if ((pin1_len != pin2_len) || (memcmp(passcode, trackp->passcode, pin1_len) != 0)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "New SecurID PIN Failed for user " "%s: PIN mis-match", user); break; } retval = SD_Pin(trackp->handle, passcode); SD_Close(trackp->handle); if (retval == ACM_NEW_PIN_ACCEPTED) { enc_tkt_reply->flags|= TKT_FLG_HW_AUTH; enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH; krb5_klog_syslog(LOG_INFO, "SecurID PIN Accepted for %s in " "verify_securid_data_2", securid_user); retval = 0; } else { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "SecurID PIN Failed for user %s (AceServer " "returns %d) in verify_securid_data_2", user, retval); } break; } case SECURID_STATE_NEW_PIN: { krb5_sam_challenge_2_body sc2b; sc2p = k5alloc(sizeof *sc2p, &retval); if (retval) goto cleanup; memset(sc2p, 0, sizeof(*sc2p)); memset(&sc2b, 0, sizeof(sc2b)); sc2b.sam_type = PA_SAM_TYPE_SECURID; sc2b.sam_response_prompt.data = NEW_PIN_AGAIN_message; sc2b.sam_response_prompt.length = strlen(sc2b.sam_response_prompt.data); sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; sc2b.sam_etype = client_key.enctype; tmp_data.data = (char *)&sc2b.sam_nonce; tmp_data.length = sizeof(sc2b.sam_nonce); if ((retval = krb5_c_random_make_octets(context, &tmp_data))) { com_err("krb5kdc", retval, "while making nonce for SecurID new " "PIN2 SAM_CHALLENGE_2 (%s)", user); goto cleanup; } sid_track_data.state = SECURID_STATE_NEW_PIN_AGAIN; sid_track_data.handle = trackp->handle; sid_track_data.hostid = gethostid(); /* Should we complain if sizes don't work ?? */ memcpy(sid_track_data.passcode, passcode, sizeof(sid_track_data.passcode)); tmp_data.data = (char *)&sid_track_data; tmp_data.length = sizeof(sid_track_data); if ((retval = securid_encrypt_track_data_2(context, client, &tmp_data, &sc2b.sam_track_id))) { com_err("krb5kdc", retval, "while encrypting NEW PIN2 SecurID " "track data for SAM_CHALLENGE_2 (%s)", securid_user); goto cleanup; } retval = sam_make_challenge(context, &sc2b, &client_key, sc2p); if (retval) { com_err("krb5kdc", retval, "while making cksum for " "SAM_CHALLENGE_2 (new PIN2) (%s)", securid_user); goto cleanup; } krb5_klog_syslog(LOG_INFO, "Requesting verification of new PIN for user %s", securid_user); *sc2_out = sc2p; sc2p = NULL; /*sc2_out may be set even on error path*/ retval = KRB5KDC_ERR_PREAUTH_REQUIRED; goto cleanup; } case SECURID_STATE_NEXT_CODE: trackp->handle = ntohl(trackp->handle); retval = SD_Next(trackp->handle, passcode); SD_Close(trackp->handle); if (retval == ACM_OK) { enc_tkt_reply->flags |= TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH; krb5_klog_syslog(LOG_INFO, "Next SecurID Code Accepted for " "user %s", securid_user); retval = 0; } else { krb5_klog_syslog(LOG_INFO, "Next SecurID Code Failed for user " "%s (AceServer returns %d) in " "verify_securid_data_2", user, retval); retval = KRB5KDC_ERR_PREAUTH_FAILED; } break; } } else { /* No track data, this is first of N attempts */ initial: retval = SD_Init(&sd_handle); if (retval) { com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "SD_Init() returns error %d in verify_securid_data_2 (%s)", retval, securid_user); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } retval = SD_Lock(sd_handle, securid_user); if (retval != ACM_OK) { SD_Close(sd_handle); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "SD_Lock() failed (AceServer returns %d) for %s", retval, securid_user); goto cleanup; } retval = SD_Check(sd_handle, passcode, securid_user); switch (retval) { case ACM_OK: SD_Close(sd_handle); enc_tkt_reply->flags|= TKT_FLG_HW_AUTH; enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH; krb5_klog_syslog(LOG_INFO, "SecurID passcode accepted for user %s", user); retval = 0; break; case ACM_ACCESS_DENIED: SD_Close(sd_handle); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "AceServer returns Access Denied for " "user %s (SAM2)", user); goto cleanup; case ACM_NEW_PIN_REQUIRED: new_pin = 1; /*fall through*/ case ACM_NEXT_CODE_REQUIRED: { krb5_sam_challenge_2_body sc2b; sc2p = k5alloc(sizeof *sc2p, &retval); if (retval) goto cleanup; memset(sc2p, 0, sizeof(*sc2p)); memset(&sc2b, 0, sizeof(sc2b)); sc2b.sam_type = PA_SAM_TYPE_SECURID; sc2b.sam_response_prompt.data = NEXT_PASSCODE_message; sc2b.sam_response_prompt.length = strlen(sc2b.sam_response_prompt.data); sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; sc2b.sam_etype = client_key.enctype; if (new_pin) { if ((AceGetMaxPinLen(sd_handle, &max_pin_len) == ACE_SUCCESS) && (AceGetMinPinLen(sd_handle, &min_pin_len) == ACE_SUCCESS) && (AceGetAlphanumeric(sd_handle, &alpha_pin) == ACE_SUCCESS)) { sprintf(PIN_message, "New PIN must contain %d to %d %sdigits", min_pin_len, max_pin_len, (alpha_pin == 0) ? "" : "alphanumeric "); sc2b.sam_challenge_label.data = PIN_message; sc2b.sam_challenge_label.length = strlen(sc2b.sam_challenge_label.data); } else { sc2b.sam_challenge_label.length = 0; } } tmp_data.data = (char *)&sc2b.sam_nonce; tmp_data.length = sizeof(sc2b.sam_nonce); if ((retval = krb5_c_random_make_octets(context, &tmp_data))) { com_err("krb5kdc", retval, "while making nonce for SecurID SAM_CHALLENGE_2 (%s)", user); goto cleanup; } if (new_pin) sid_track_data.state = SECURID_STATE_NEW_PIN; else sid_track_data.state = SECURID_STATE_NEXT_CODE; sid_track_data.handle = htonl(sd_handle); sid_track_data.hostid = gethostid(); tmp_data.data = (char *)&sid_track_data; tmp_data.length = sizeof(sid_track_data); retval = securid_encrypt_track_data_2(context, client, &tmp_data, &sc2b.sam_track_id); if (retval) { com_err("krb5kdc", retval, "while encrypting SecurID track " "data for SAM_CHALLENGE_2 (%s)", securid_user); goto cleanup; } retval = sam_make_challenge(context, &sc2b, &client_key, sc2p); if (retval) { com_err("krb5kdc", retval, "while making cksum for SAM_CHALLENGE_2 (%s)", securid_user); } if (new_pin) krb5_klog_syslog(LOG_INFO, "New SecurID PIN required for " "user %s", securid_user); else krb5_klog_syslog(LOG_INFO, "Next SecurID passcode required " "for user %s", securid_user); *sc2_out = sc2p; sc2p = NULL; retval = KRB5KDC_ERR_PREAUTH_REQUIRED; /*sc2_out is permitted as an output on error path*/ goto cleanup; } default: com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "AceServer returns unknown error code %d " "in verify_securid_data_2\n", retval); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } } /* no track_id data */ cleanup: krb5_free_keyblock_contents(context, &client_key); free(scratch.data); krb5_free_enc_sam_response_enc_2(context, esre2); free(user); free(securid_user); free(trackp); krb5_free_sam_challenge_2(context, sc2p); return retval; }
/* Produce a list of credentials from a KRB-CRED message and its enc_part. */ static krb5_error_code make_cred_list(krb5_context context, krb5_cred *krbcred, krb5_cred_enc_part *encpart, krb5_creds ***creds_out) { krb5_error_code ret = 0; krb5_creds **list = NULL; krb5_cred_info *info; krb5_data *ticket_data; size_t i, count; *creds_out = NULL; /* Allocate the list of creds. */ for (count = 0; krbcred->tickets[count] != NULL; count++); list = k5calloc(count + 1, sizeof(*list), &ret); if (list == NULL) goto cleanup; /* For each credential, create a strcture in the list of credentials and * copy the information. */ for (i = 0; i < count; i++) { list[i] = k5alloc(sizeof(*list[i]), &ret); if (list[i] == NULL) goto cleanup; info = encpart->ticket_info[i]; ret = krb5_copy_principal(context, info->client, &list[i]->client); if (ret) goto cleanup; ret = krb5_copy_principal(context, info->server, &list[i]->server); if (ret) goto cleanup; ret = krb5_copy_keyblock_contents(context, info->session, &list[i]->keyblock); if (ret) goto cleanup; ret = krb5_copy_addresses(context, info->caddrs, &list[i]->addresses); if (ret) goto cleanup; ret = encode_krb5_ticket(krbcred->tickets[i], &ticket_data); if (ret) goto cleanup; list[i]->ticket = *ticket_data; free(ticket_data); list[i]->is_skey = FALSE; list[i]->magic = KV5M_CREDS; list[i]->times = info->times; list[i]->ticket_flags = info->flags; list[i]->authdata = NULL; list[i]->second_ticket = empty_data(); } *creds_out = list; list = NULL; cleanup: krb5_free_tgt_creds(context, list); return ret; }
/*ARGSUSED*/ void process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, const krb5_fulladdr *from, kdc_realm_t *kdc_active_realm, verto_ctx *vctx, loop_respond_fn respond, void *arg) { krb5_error_code errcode; unsigned int s_flags = 0; krb5_data encoded_req_body; krb5_enctype useenctype; struct as_req_state *state; state = k5alloc(sizeof(*state), &errcode); if (state == NULL) { (*respond)(arg, errcode, NULL); return; } state->respond = respond; state->arg = arg; state->request = request; state->req_pkt = req_pkt; state->from = from; state->active_realm = kdc_active_realm; errcode = kdc_make_rstate(kdc_active_realm, &state->rstate); if (errcode != 0) { (*respond)(arg, errcode, NULL); return; } if (state->request->msg_type != KRB5_AS_REQ) { state->status = "msg_type mismatch"; errcode = KRB5_BADMSGTYPE; goto errout; } if (fetch_asn1_field((unsigned char *) req_pkt->data, 1, 4, &encoded_req_body) != 0) { errcode = ASN1_BAD_ID; state->status = "Finding req_body"; goto errout; } errcode = kdc_find_fast(&state->request, &encoded_req_body, NULL, NULL, state->rstate, &state->inner_body); if (errcode) { state->status = "error decoding FAST"; goto errout; } if (state->inner_body == NULL) { /* Not a FAST request; copy the encoded request body. */ errcode = krb5_copy_data(kdc_context, &encoded_req_body, &state->inner_body); if (errcode) { state->status = "storing req body"; goto errout; } } state->rock.request = state->request; state->rock.inner_body = state->inner_body; state->rock.rstate = state->rstate; state->rock.vctx = vctx; if (!state->request->client) { state->status = "NULL_CLIENT"; errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, state->request->client, &state->cname))) { state->status = "UNPARSING_CLIENT"; goto errout; } limit_string(state->cname); if (!state->request->server) { state->status = "NULL_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, state->request->server, &state->sname))) { state->status = "UNPARSING_SERVER"; goto errout; } limit_string(state->sname); /* * We set KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY as a hint * to the backend to return naming information in lieu * of cross realm TGS entries. */ setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY); /* * Note that according to the referrals draft we should * always canonicalize enterprise principal names. */ if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) || state->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL) { setflag(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE); setflag(state->c_flags, KRB5_KDB_FLAG_ALIAS_OK); } if (include_pac_p(kdc_context, state->request)) { setflag(state->c_flags, KRB5_KDB_FLAG_INCLUDE_PAC); } errcode = krb5_db_get_principal(kdc_context, state->request->client, state->c_flags, &state->client); if (errcode == KRB5_KDB_CANTLOCK_DB) errcode = KRB5KDC_ERR_SVC_UNAVAILABLE; if (errcode == KRB5_KDB_NOENTRY) { state->status = "CLIENT_NOT_FOUND"; if (vague_errors) errcode = KRB5KRB_ERR_GENERIC; else errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { state->status = "LOOKING_UP_CLIENT"; goto errout; } state->rock.client = state->client; /* * If the backend returned a principal that is not in the local * realm, then we need to refer the client to that realm. */ if (!is_local_principal(kdc_active_realm, state->client->princ)) { /* Entry is a referral to another realm */ state->status = "REFERRAL"; errcode = KRB5KDC_ERR_WRONG_REALM; goto errout; } s_flags = 0; setflag(s_flags, KRB5_KDB_FLAG_ALIAS_OK); if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) { setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE); } errcode = krb5_db_get_principal(kdc_context, state->request->server, s_flags, &state->server); if (errcode == KRB5_KDB_CANTLOCK_DB) errcode = KRB5KDC_ERR_SVC_UNAVAILABLE; if (errcode == KRB5_KDB_NOENTRY) { state->status = "SERVER_NOT_FOUND"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { state->status = "LOOKING_UP_SERVER"; goto errout; } if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time))) { state->status = "TIMEOFDAY"; goto errout; } state->authtime = state->kdc_time; /* for audit_as_request() */ if ((errcode = validate_as_request(kdc_active_realm, state->request, *state->client, *state->server, state->kdc_time, &state->status, &state->e_data))) { if (!state->status) state->status = "UNKNOWN_REASON"; errcode += ERROR_TABLE_BASE_krb5; goto errout; } /* * Select the keytype for the ticket session key. */ if ((useenctype = select_session_keytype(kdc_active_realm, state->server, state->request->nktypes, state->request->ktype)) == 0) { /* unsupported ktype */ state->status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto errout; } if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, &state->session_key))) { state->status = "RANDOM_KEY_FAILED"; goto errout; } /* * Canonicalization is only effective if we are issuing a TGT * (the intention is to allow support for Windows "short" realm * aliases, nothing more). */ if (isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE) && krb5_is_tgs_principal(state->request->server) && krb5_is_tgs_principal(state->server->princ)) { state->ticket_reply.server = state->server->princ; } else { state->ticket_reply.server = state->request->server; } state->enc_tkt_reply.flags = 0; state->enc_tkt_reply.times.authtime = state->authtime; setflag(state->enc_tkt_reply.flags, TKT_FLG_INITIAL); setflag(state->enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP); /* * It should be noted that local policy may affect the * processing of any of these flags. For example, some * realms may refuse to issue renewable tickets */ if (isflagset(state->request->kdc_options, KDC_OPT_FORWARDABLE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); if (isflagset(state->request->kdc_options, KDC_OPT_PROXIABLE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(state->request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); state->enc_tkt_reply.session = &state->session_key; if (isflagset(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE)) { state->client_princ = *(state->client->princ); } else { state->client_princ = *(state->request->client); /* The realm is always canonicalized */ state->client_princ.realm = state->client->princ->realm; } state->enc_tkt_reply.client = &state->client_princ; state->enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; state->enc_tkt_reply.transited.tr_contents = empty_string; if (isflagset(state->request->kdc_options, KDC_OPT_POSTDATED)) { setflag(state->enc_tkt_reply.flags, TKT_FLG_POSTDATED); setflag(state->enc_tkt_reply.flags, TKT_FLG_INVALID); state->enc_tkt_reply.times.starttime = state->request->from; } else state->enc_tkt_reply.times.starttime = state->kdc_time; kdc_get_ticket_endtime(kdc_active_realm, state->enc_tkt_reply.times.starttime, kdc_infinity, state->request->till, state->client, state->server, &state->enc_tkt_reply.times.endtime); kdc_get_ticket_renewtime(kdc_active_realm, state->request, NULL, state->client, state->server, &state->enc_tkt_reply); /* * starttime is optional, and treated as authtime if not present. * so we can nuke it if it matches */ if (state->enc_tkt_reply.times.starttime == state->enc_tkt_reply.times.authtime) state->enc_tkt_reply.times.starttime = 0; state->enc_tkt_reply.caddrs = state->request->addresses; state->enc_tkt_reply.authorization_data = 0; /* If anonymous requests are being used, adjust the realm of the client * principal. */ if (isflagset(state->request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) { if (!krb5_principal_compare_any_realm(kdc_context, state->request->client, krb5_anonymous_principal())) { errcode = KRB5KDC_ERR_BADOPTION; state->status = "Anonymous requested but anonymous " "principal not used."; goto errout; } setflag(state->enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); krb5_free_principal(kdc_context, state->request->client); state->request->client = NULL; errcode = krb5_copy_principal(kdc_context, krb5_anonymous_principal(), &state->request->client); if (errcode) { state->status = "Copying anonymous principal"; goto errout; } state->enc_tkt_reply.client = state->request->client; setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH); } /* * Check the preauthentication if it is there. */ if (state->request->padata) { check_padata(kdc_context, &state->rock, state->req_pkt, state->request, &state->enc_tkt_reply, &state->pa_context, &state->e_data, &state->typed_e_data, finish_preauth, state); } else finish_preauth(state, 0); return; errout: finish_process_as_req(state, errcode); }
OM_uint32 KRB5_CALLCONV krb5_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context, int prf_key, const gss_buffer_t prf_in, ssize_t desired_output_len, gss_buffer_t prf_out) { krb5_error_code code; krb5_key key = NULL; krb5_gss_ctx_id_t ctx; int i; OM_uint32 minor; size_t prflen; krb5_data t, ns; unsigned char *p; prf_out->length = 0; prf_out->value = NULL; t.length = 0; t.data = NULL; ns.length = 0; ns.data = NULL; ctx = (krb5_gss_ctx_id_t)context; switch (prf_key) { case GSS_C_PRF_KEY_FULL: if (ctx->have_acceptor_subkey) { key = ctx->acceptor_subkey; break; } /* fallthrough */ case GSS_C_PRF_KEY_PARTIAL: key = ctx->subkey; break; default: code = EINVAL; goto cleanup; } if (key == NULL) { code = EINVAL; goto cleanup; } if (desired_output_len == 0) return GSS_S_COMPLETE; prf_out->value = k5alloc(desired_output_len, &code); if (prf_out->value == NULL) { code = KG_INPUT_TOO_LONG; goto cleanup; } prf_out->length = desired_output_len; code = krb5_c_prf_length(ctx->k5_context, krb5_k_key_enctype(ctx->k5_context, key), &prflen); if (code != 0) goto cleanup; ns.length = 4 + prf_in->length; ns.data = k5alloc(ns.length, &code); if (ns.data == NULL) { code = KG_INPUT_TOO_LONG; goto cleanup; } t.length = prflen; t.data = k5alloc(t.length, &code); if (t.data == NULL) goto cleanup; memcpy(ns.data + 4, prf_in->value, prf_in->length); i = 0; p = (unsigned char *)prf_out->value; while (desired_output_len > 0) { store_32_be(i, ns.data); code = krb5_k_prf(ctx->k5_context, key, &ns, &t); if (code != 0) goto cleanup; memcpy(p, t.data, MIN(t.length, desired_output_len)); p += t.length; desired_output_len -= t.length; i++; } cleanup: if (code != 0) gss_release_buffer(&minor, prf_out); krb5_free_data_contents(ctx->k5_context, &ns); krb5_free_data_contents(ctx->k5_context, &t); *minor_status = (OM_uint32)code; return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; }
static krb5_error_code encts_process(krb5_context context, krb5_clpreauth_moddata moddata, krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *padata, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***out_padata) { krb5_error_code ret; krb5_pa_enc_ts pa_enc; krb5_data *ts = NULL, *enc_ts = NULL; krb5_enc_data enc_data; krb5_pa_data **pa = NULL; krb5_keyblock *as_key; enc_data.ciphertext = empty_data(); ret = cb->get_as_key(context, rock, &as_key); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key); /* * Try and use the timestamp of the preauth request, even if it's * unauthenticated. We could be fooled into making a preauth response for * a future time, but that has no security consequences other than the * KDC's audit logs. If kdc_timesync is not configured, then this will * just use local time. */ ret = cb->get_preauth_time(context, rock, TRUE, &pa_enc.patimestamp, &pa_enc.pausec); if (ret) goto cleanup; ret = encode_krb5_pa_enc_ts(&pa_enc, &ts); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, as_key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, ts, &enc_data); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS(context, pa_enc.patimestamp, pa_enc.pausec, ts, &enc_data.ciphertext); ret = encode_krb5_enc_data(&enc_data, &enc_ts); if (ret) goto cleanup; pa = k5alloc(2 * sizeof(krb5_pa_data *), &ret); if (pa == NULL) goto cleanup; pa[0] = k5alloc(sizeof(krb5_pa_data), &ret); if (pa[0] == NULL) goto cleanup; pa[0]->magic = KV5M_PA_DATA; pa[0]->pa_type = KRB5_PADATA_ENC_TIMESTAMP; pa[0]->length = enc_ts->length; pa[0]->contents = (krb5_octet *) enc_ts->data; enc_ts->data = NULL; pa[1] = NULL; *out_padata = pa; pa = NULL; cleanup: krb5_free_data(context, ts); krb5_free_data(context, enc_ts); free(enc_data.ciphertext.data); free(pa); return ret; }
/* * Map the log file to memory for performance and simplicity. * * Called by: if iprop_enabled then ulog_map(); * Assumes that the caller will terminate on ulog_map, hence munmap and * closing of the fd are implicitly performed by the caller. */ krb5_error_code ulog_map(krb5_context context, const char *logname, uint32_t ulogentries) { struct stat st; krb5_error_code retval; uint32_t filesize; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; int ulogfd = -1; if (stat(logname, &st) == -1) { ulogfd = open(logname, O_RDWR | O_CREAT, 0600); if (ulogfd == -1) return errno; filesize = sizeof(kdb_hlog_t) + ulogentries * ULOG_BLOCK; if (extend_file_to(ulogfd, filesize) < 0) return errno; } else { ulogfd = open(logname, O_RDWR, 0600); if (ulogfd == -1) return errno; } ulog = mmap(0, MAXLOGLEN, PROT_READ | PROT_WRITE, MAP_SHARED, ulogfd, 0); if (ulog == MAP_FAILED) { /* Can't map update log file to memory. */ close(ulogfd); return errno; } if (!context->kdblog_context) { log_ctx = k5alloc(sizeof(kdb_log_context), &retval); if (log_ctx == NULL) return retval; memset(log_ctx, 0, sizeof(*log_ctx)); context->kdblog_context = log_ctx; } else { log_ctx = context->kdblog_context; } log_ctx->ulog = ulog; log_ctx->ulogentries = ulogentries; log_ctx->ulogfd = ulogfd; retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); if (retval) return retval; if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC) { if (ulog->kdb_hmagic != 0) { unlock_ulog(context); return KRB5_LOG_CORRUPT; } reset_header(ulog); sync_header(ulog); } /* Reinit ulog if ulogentries changed such that we have too many entries or * our first or last entry was written to the wrong location. */ if (ulog->kdb_num != 0 && (ulog->kdb_num > ulogentries || !check_sno(log_ctx, ulog->kdb_first_sno, &ulog->kdb_first_time) || !check_sno(log_ctx, ulog->kdb_last_sno, &ulog->kdb_last_time))) { reset_header(ulog); sync_header(ulog); } if (ulog->kdb_num != ulogentries) { /* Expand the ulog file if it isn't big enough. */ filesize = sizeof(kdb_hlog_t) + ulogentries * ulog->kdb_block; if (extend_file_to(ulogfd, filesize) < 0) { unlock_ulog(context); return errno; } } unlock_ulog(context); return 0; }
static void otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_verify_respond_fn respond, void *arg) { krb5_keyblock *armor_key = NULL; krb5_pa_otp_req *req = NULL; struct request_state *rs; krb5_error_code retval; krb5_data d, plaintext; char *config; /* Get the FAST armor key. */ armor_key = cb->fast_armor(context, rock); if (armor_key == NULL) { retval = KRB5KDC_ERR_PREAUTH_FAILED; com_err("otp", retval, "No armor key found when verifying padata"); goto error; } /* Decode the request. */ d = make_data(pa->contents, pa->length); retval = decode_krb5_pa_otp_req(&d, &req); if (retval != 0) { com_err("otp", retval, "Unable to decode OTP request"); goto error; } /* Decrypt the nonce from the request. */ retval = decrypt_encdata(context, armor_key, req, &plaintext); if (retval != 0) { com_err("otp", retval, "Unable to decrypt nonce"); goto error; } /* Verify the nonce or timestamp. */ retval = nonce_verify(context, armor_key, &plaintext); if (retval != 0) retval = timestamp_verify(context, &plaintext); krb5_free_data_contents(context, &plaintext); if (retval != 0) { com_err("otp", retval, "Unable to verify nonce or timestamp"); goto error; } /* Create the request state. Save the response callback, and the * enc_tkt_reply pointer so we can set the TKT_FLG_PRE_AUTH flag later. */ rs = k5alloc(sizeof(struct request_state), &retval); if (rs == NULL) goto error; rs->context = context; rs->arg = arg; rs->respond = respond; rs->enc_tkt_reply = enc_tkt_reply; rs->preauth_cb = cb; rs->rock = rock; /* Get the principal's OTP configuration string. */ retval = cb->get_string(context, rock, "otp", &config); if (retval == 0 && config == NULL) retval = KRB5_PREAUTH_FAILED; if (retval != 0) { free(rs); goto error; } /* Send the request. */ otp_state_verify((otp_state *)moddata, cb->event_context(context, rock), request->client, config, req, on_response, rs); cb->free_string(context, rock, config); k5_free_pa_otp_req(context, req); return; error: k5_free_pa_otp_req(context, req); (*respond)(arg, retval, NULL, NULL, NULL); }
krb5_error_code krb5_decode_princ_entry(krb5_context context, krb5_data *content, krb5_db_entry **entry_ptr) { int sizeleft, i; unsigned char * nextloc; krb5_tl_data ** tl_data; krb5_int16 i16; krb5_db_entry * entry; krb5_error_code retval; *entry_ptr = NULL; entry = k5alloc(sizeof(*entry), &retval); if (entry == NULL) return retval; /* * Reverse the encoding of encode_princ_entry. * * The first part is decoding the base type. If the base type is * bigger than the original base type then the additional fields * need to be filled in. If the base type is larger than any * known base type the additional data goes in e_data. */ /* First do the easy stuff */ nextloc = (unsigned char *)content->data; sizeleft = content->length; if ((sizeleft -= KRB5_KDB_V1_BASE_LENGTH) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } /* Base Length */ krb5_kdb_decode_int16(nextloc, entry->len); nextloc += 2; /* Attributes */ krb5_kdb_decode_int32(nextloc, entry->attributes); nextloc += 4; /* Max Life */ krb5_kdb_decode_int32(nextloc, entry->max_life); nextloc += 4; /* Max Renewable Life */ krb5_kdb_decode_int32(nextloc, entry->max_renewable_life); nextloc += 4; /* When the client expires */ krb5_kdb_decode_int32(nextloc, entry->expiration); nextloc += 4; /* When its passwd expires */ krb5_kdb_decode_int32(nextloc, entry->pw_expiration); nextloc += 4; /* Last successful passwd */ krb5_kdb_decode_int32(nextloc, entry->last_success); nextloc += 4; /* Last failed passwd attempt */ krb5_kdb_decode_int32(nextloc, entry->last_failed); nextloc += 4; /* # of failed passwd attempt */ krb5_kdb_decode_int32(nextloc, entry->fail_auth_count); nextloc += 4; /* # tl_data strutures */ krb5_kdb_decode_int16(nextloc, entry->n_tl_data); nextloc += 2; if (entry->n_tl_data < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } /* # key_data strutures */ krb5_kdb_decode_int16(nextloc, entry->n_key_data); nextloc += 2; if (entry->n_key_data < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } /* Check for extra data */ if (entry->len > KRB5_KDB_V1_BASE_LENGTH) { entry->e_length = entry->len - KRB5_KDB_V1_BASE_LENGTH; entry->e_data = k5memdup(nextloc, entry->e_length, &retval); if (entry->e_data == NULL) goto error_out; nextloc += entry->e_length; } /* * Get the principal name for the entry * (stored as a string which gets unparsed.) */ if ((sizeleft -= 2) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } i = 0; krb5_kdb_decode_int16(nextloc, i16); i = (int) i16; nextloc += 2; if ((retval = krb5_parse_name(context, (char *)nextloc, &(entry->princ)))) goto error_out; if (((size_t) i != (strlen((char *)nextloc) + 1)) || (sizeleft < i)) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } sizeleft -= i; nextloc += i; /* tl_data is a linked list */ tl_data = &entry->tl_data; for (i = 0; i < entry->n_tl_data; i++) { if ((sizeleft -= 4) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } if ((*tl_data = (krb5_tl_data *) malloc(sizeof(krb5_tl_data))) == NULL) { retval = ENOMEM; goto error_out; } (*tl_data)->tl_data_next = NULL; (*tl_data)->tl_data_contents = NULL; krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_type); nextloc += 2; krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_length); nextloc += 2; if ((sizeleft -= (*tl_data)->tl_data_length) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } (*tl_data)->tl_data_contents = k5memdup(nextloc, (*tl_data)->tl_data_length, &retval); if ((*tl_data)->tl_data_contents == NULL) goto error_out; nextloc += (*tl_data)->tl_data_length; tl_data = &((*tl_data)->tl_data_next); } /* key_data is an array */ if (entry->n_key_data && ((entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * entry->n_key_data)) == NULL)) { retval = ENOMEM; goto error_out; } for (i = 0; i < entry->n_key_data; i++) { krb5_key_data * key_data; int j; if ((sizeleft -= 4) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } key_data = entry->key_data + i; memset(key_data, 0, sizeof(krb5_key_data)); krb5_kdb_decode_int16(nextloc, key_data->key_data_ver); nextloc += 2; krb5_kdb_decode_int16(nextloc, key_data->key_data_kvno); nextloc += 2; /* key_data_ver determins number of elements and how to unparse them. */ if (key_data->key_data_ver <= KRB5_KDB_V1_KEY_DATA_ARRAY) { for (j = 0; j < key_data->key_data_ver; j++) { if ((sizeleft -= 4) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } krb5_kdb_decode_int16(nextloc, key_data->key_data_type[j]); nextloc += 2; krb5_kdb_decode_int16(nextloc, key_data->key_data_length[j]); nextloc += 2; if ((sizeleft -= key_data->key_data_length[j]) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } if (key_data->key_data_length[j]) { key_data->key_data_contents[j] = k5memdup(nextloc, key_data->key_data_length[j], &retval); if (key_data->key_data_contents[j] == NULL) goto error_out; nextloc += key_data->key_data_length[j]; } } } else { /* This isn't right. I'll fix it later */ abort(); } } *entry_ptr = entry; return 0; error_out: krb5_dbe_free(context, entry); return retval; }
/* Used by the slave to update its hash db from the incr update log. */ krb5_error_code ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args) { krb5_db_entry *entry = NULL; kdb_incr_update_t *upd = NULL, *fupd; int i, no_of_updates; krb5_error_code retval; krb5_principal dbprinc; char *dbprincstr; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; INIT_ULOG(context); /* Lock the DB before the ulog to avoid deadlock. */ retval = krb5_db_open(context, db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); if (retval) return retval; retval = krb5_db_lock(context, KRB5_DB_LOCKMODE_EXCLUSIVE); if (retval) return retval; retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); if (retval) { krb5_db_unlock(context); return retval; } no_of_updates = incr_ret->updates.kdb_ulog_t_len; upd = incr_ret->updates.kdb_ulog_t_val; fupd = upd; for (i = 0; i < no_of_updates; i++) { if (!upd->kdb_commit) continue; /* If (unexpectedly) this update does not follow the last one we * stored, discard any previous ulog state. */ if (ulog->kdb_num != 0 && upd->kdb_entry_sno != ulog->kdb_last_sno + 1) reset_header(ulog); if (upd->kdb_deleted) { dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val, upd->kdb_princ_name.utf8str_t_len, &retval); if (dbprincstr == NULL) goto cleanup; retval = krb5_parse_name(context, dbprincstr, &dbprinc); free(dbprincstr); if (retval) goto cleanup; retval = krb5int_delete_principal_no_log(context, dbprinc); krb5_free_principal(context, dbprinc); if (retval == KRB5_KDB_NOENTRY) retval = 0; if (retval) goto cleanup; } else { entry = k5alloc(sizeof(krb5_db_entry), &retval); if (entry == NULL) goto cleanup; retval = ulog_conv_2dbentry(context, &entry, upd); if (retval) goto cleanup; retval = krb5int_put_principal_no_log(context, entry); krb5_db_free_principal(context, entry); if (retval) goto cleanup; } retval = store_update(log_ctx, upd); if (retval) goto cleanup; upd++; } cleanup: if (fupd) ulog_free_entries(fupd, no_of_updates); if (retval) { reset_header(ulog); sync_header(ulog); } unlock_ulog(context); krb5_db_unlock(context); return retval; }
krb5_error_code kh_marshal_hdb_entry(krb5_context context, const krb5_db_entry *kentry, hdb_entry *hentry) { kh_db_context *kh = KH_DB_CONTEXT(context); krb5_error_code code; krb5_int16 kvno = 0; int i; memset(hentry, 0, sizeof(*hentry)); code = kh_marshal_Principal(context, kentry->princ, &hentry->principal); if (code != 0) goto cleanup; code = kh_marshal_HDBFlags(context, kentry->attributes, &hentry->flags); if (code != 0) goto cleanup; if (kentry->expiration) { hentry->valid_end = k5alloc(sizeof(KerberosTime), &code); if (code != 0) goto cleanup; *(hentry->valid_end) = kentry->expiration; } if (kentry->pw_expiration) { hentry->pw_end = k5alloc(sizeof(KerberosTime), &code); if (code != 0) goto cleanup; *(hentry->pw_end) = kentry->pw_expiration; } if (kentry->max_life) { hentry->max_life = k5alloc(sizeof(unsigned int), &code); if (code != 0) goto cleanup; *(hentry->max_life) = kentry->max_life; } if (kentry->max_renewable_life) { hentry->max_renew = k5alloc(sizeof(unsigned int), &code); if (code != 0) goto cleanup; *(hentry->max_renew) = kentry->max_renewable_life; } /* last_success */ /* last_failed */ /* fail_auth_count */ /* n_tl_data */ if ((kentry->attributes & KRB5_KDB_NEW_PRINC) == 0) { hentry->modified_by = k5alloc(sizeof(Event), &code); if (code != 0) goto cleanup; code = kh_marshal_Event(context, kentry, hentry->modified_by); } else { code = kh_marshal_Event(context, kentry, &hentry->created_by); } if (code != 0) goto cleanup; hentry->extensions = k5alloc(sizeof(HDB_extensions), &code); if (code != 0) goto cleanup; code = kh_marshal_HDB_extensions(context, kentry, hentry->extensions); if (code != 0) goto cleanup; hentry->keys.len = 0; hentry->keys.val = k5alloc(kentry->n_key_data * sizeof(Key), &code); if (code != 0) goto cleanup; for (i = 0; i < kentry->n_key_data; i++) { code = kh_marshal_Key(context, &kentry->key_data[i], &hentry->keys.val[hentry->keys.len]); if (code != 0) goto cleanup; if (kentry->key_data[i].key_data_kvno > kvno) kvno = kentry->key_data[i].key_data_kvno; hentry->keys.len++; } hentry->kvno = kvno; cleanup: if (code != 0) { hdb_entry_ex hext; hext.ctx = NULL; hext.entry = *hentry; hext.free_entry = NULL; kh_hdb_free_entry(context, kh, &hext); memset(hentry, 0, sizeof(*hentry)); } return code; }