From 163d95cecc338338d72817573b7079d641e8b31f Mon Sep 17 00:00:00 2001 From: Theodore Tso Date: Wed, 8 Nov 1995 07:52:57 +0000 Subject: * encrypt_tk.c (cleanup_scratch): Changed interface to no longer require an eblock; we can use our own and figure out the enctype from the passed-in key. * get_in_tkt.c (krb5_get_in_tkt): Added calls to krb5_obtain_padata(). * preauth.c: Completely restructured file to support preauthentication. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@7056 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/krb5/krb/preauth.c | 585 +++++++++++++++------------------------------ 1 file changed, 192 insertions(+), 393 deletions(-) (limited to 'src/lib/krb5/krb/preauth.c') diff --git a/src/lib/krb5/krb/preauth.c b/src/lib/krb5/krb/preauth.c index e41399a..ae961a6 100644 --- a/src/lib/krb5/krb/preauth.c +++ b/src/lib/krb5/krb/preauth.c @@ -18,25 +18,15 @@ * this software for any purpose. It is provided "as is" without express * or implied warranty. * - * Sandia National Laboratories also makes no representations about the - * suitability of the modifications, or additions to this software for - * any purpose. It is provided "as is" without express or implied warranty. - * - * Note: The internal interfaces to this routine are subject to change - * and/or cleanup. You should only expect the interfaces to - * krb5_obtain_padata and krb5_verify_padata to have some chance of - * staying stable. [tytso:19920903.1544EDT] */ /* * This file contains routines for establishing, verifying, and any other * necessary functions, for utilizing the pre-authentication field of the * kerberos kdc request, with various hardware/software verification devices. - * - * Note: At some point these functions may very well be split apart - * into different files.... [tytso:19920903.1618EDT] */ + #include "k5-int.h" #include #include @@ -46,437 +36,246 @@ #include #endif +static krb5_preauth_obtain_proc obtain_enc_ts_padata; + static krb5_preauth_ops preauth_systems[] = { { - 0, - KRB5_PADATA_ENC_UNIX_TIME, - KRB5_PREAUTH_FLAGS_ENCRYPT, - get_unixtime_padata, - verify_unixtime_padata, - }, - { - 0, - KRB5_PADATA_ENC_SANDIA_SECURID, - KRB5_PREAUTH_FLAGS_ENCRYPT | KRB5_PREAUTH_FLAGS_HARDWARE, - get_securid_padata, - verify_securid_padata, + KV5M_PREAUTH_OPS, + KRB5_PADATA_ENC_TIMESTAMP, + 0, + obtain_enc_ts_padata, + 0, }, { -1,} }; -static krb5_error_code find_preauthenticator +static krb5_error_code find_pa_system PROTOTYPE((int type, krb5_preauth_ops **Preauth_proc)); -/* - * krb5_obtain_padata is a glue routine which when passed in - * a preauthentication type, client principal, and src_addr, returns - * preauthentication data contained in data to be passed onto the KDC. - * - * If problems occur then a non zero value is returned... - * - * Note: This is a first crack at what any preauthentication will need... - */ +/* some typedef's for the function args to make things look a bit cleaner */ + +typedef krb5_error_code (*git_key_proc) PROTOTYPE((krb5_context, + const krb5_enctype, + krb5_data *, + krb5_const_pointer, + krb5_keyblock **)); + krb5_error_code -krb5_obtain_padata(context, type, client, src_addr, encrypt_key, ret_data) - krb5_context context; - int type; /*IN: Preauth type */ - krb5_principal client; /*IN: requestor */ - krb5_address **src_addr; /*IN: array of ptrs to addresses */ - krb5_keyblock *encrypt_key; /*IN: encryption key */ - krb5_pa_data **ret_data; /*OUT: Returned padata */ +krb5_encrypt_data(context, key, ivec, data, enc_data) + krb5_context context; + krb5_keyblock * key; + krb5_pointer ivec; + krb5_data * data; + krb5_enc_data * enc_data; { krb5_error_code retval; - krb5_preauth_ops *p_system; krb5_encrypt_block eblock; - krb5_data scratch; - krb5_pa_data *data; - if (!ret_data) - return EINVAL; - *ret_data = 0; - - if (type == KRB5_PADATA_NONE ) - return(0); + krb5_use_enctype(context, &eblock, key->enctype); - data = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); - if (!data) + enc_data->magic = KV5M_ENC_DATA; + enc_data->kvno = 0; + enc_data->enctype = key->enctype; + enc_data->ciphertext.length = krb5_encrypt_size(data->length, + eblock.crypto_entry); + enc_data->ciphertext.data = malloc(enc_data->ciphertext.length); + if (enc_data->ciphertext.data == 0) return ENOMEM; - - data->length = 0; - data->contents = 0; - data->pa_type = type; - /* Find appropriate preauthenticator */ - retval = find_preauthenticator(type, &p_system); - if (retval) - goto error_out; + if ((retval = krb5_process_key(context, &eblock, key)) != 0) + goto cleanup; - retval = (*p_system->obtain)(context, client, src_addr, data ); - if (retval) - goto error_out; - - /* Check to see if we need to encrypt padata */ - if (p_system->flags & KRB5_PREAUTH_FLAGS_ENCRYPT) { - /* If we dont have a encryption key we are out of luck */ - if (!encrypt_key) { - retval = KRB5_PREAUTH_NO_KEY; - goto error_out; - } - krb5_use_enctype(context, &eblock, encrypt_key->enctype); - - /* do any necessay key pre-processing */ - retval = krb5_process_key(context, &eblock, encrypt_key); - if (retval) - goto error_out; - - /* - * Set up scratch data and length for encryption - * Must allocate more space for checksum and confounder - * We also leave space for an uncrypted size field. - */ - scratch.length = krb5_encrypt_size(data->length, - eblock.crypto_entry) + 4; - - if(!(scratch.data = malloc(scratch.length))){ - (void) krb5_finish_key(context, &eblock); - retval = ENOMEM; - goto error_out; - } - - scratch.data[0] = data->length >> 24; - scratch.data[1] = data->length >> 16; - scratch.data[2] = data->length >> 8; - scratch.data[3] = data->length; - - /* Encrypt preauth data in encryption key */ - if ((retval = krb5_encrypt(context, (krb5_pointer) data->contents, - (char *) scratch.data + 4, - data->length, &eblock, 0))) { - (void) krb5_finish_key(context, &eblock); - free(scratch.data); - goto error_out; - } - (void) krb5_finish_key(context, &eblock); - - free(data->contents); - data->length = scratch.length; - data->contents = (unsigned char *) scratch.data; + if ((retval = krb5_encrypt(context, (krb5_pointer) data->data, + (krb5_pointer) enc_data->ciphertext.data, + data->length, &eblock, ivec))) { + krb5_finish_key(context, &eblock); + goto cleanup; } + (void) krb5_finish_key(context, &eblock); - *ret_data = data; return 0; - -error_out: - free(data); + +cleanup: + free(enc_data->ciphertext.data); return retval; } -/* - * krb5_verify_padata is a glue routine which when passed in - * the client, src_addr and padata verifies it with the appropriate - * verify function. - * - * If problems occur then a non zero value is returned... - * else returns zero if padata verifies, and returns a "unique" id. - * - * Note: This is a first crack at what any preauthentication will need... - */ - -krb5_error_code -krb5_verify_padata(context, data,client,src_addr, decrypt_key, req_id, flags) - krb5_context context; - krb5_pa_data *data; /*IN: padata */ - krb5_principal client; /*IN: requestor */ - krb5_address **src_addr; /*IN: array of ptrs to addresses */ - krb5_keyblock *decrypt_key; /*IN: decryption key */ - int * req_id; /*OUT: identifier */ - int * flags; /*OUT: flags */ + +krb5_error_code krb5_obtain_padata(context, preauth_to_use, etype_info, + key_proc, key_seed, creds, request) + krb5_context context; + krb5_pa_data ** preauth_to_use; + krb5_etype_info etype_info; + git_key_proc key_proc; + krb5_const_pointer key_seed; + krb5_creds * creds; + krb5_kdc_req * request; { - krb5_preauth_ops *p_system; - krb5_encrypt_block eblock; - krb5_data scratch; - int free_scratch = 0; - krb5_checksum cksum; - krb5_error_code retval; + krb5_error_code retval; + krb5_pa_data ** pa; + krb5_pa_data ** send_pa_list; + krb5_pa_data ** send_pa; + krb5_preauth_ops *ops; + krb5_keyblock * def_enc_key = 0; + krb5_enctype enctype; + krb5_data salt; + int size; + int f_salt = 0; + + if (preauth_to_use == NULL) + return 0; + + for (pa = preauth_to_use, size=0; *pa; pa++, size++); + + if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL) + return ENOMEM; - if (!data) - return(EINVAL); + send_pa = send_pa_list; + *send_pa = 0; - /* Find appropriate preauthenticator */ - retval = find_preauthenticator((int) data->pa_type, &p_system); - if (retval) - return retval; - - /* Check to see if we need to decrypt padata */ - if (p_system->flags & KRB5_PREAUTH_FLAGS_ENCRYPT) { - - /* If we dont have a decryption key we are out of luck */ - if (!decrypt_key) - return(EINVAL); - - krb5_use_enctype(context, &eblock, decrypt_key->enctype); - - scratch.length = data->length; - if (!(scratch.data = (char *)malloc(scratch.length))) { - return(ENOMEM); - } - - /* do any necessay key pre-processing */ - retval = krb5_process_key(context, &eblock,decrypt_key); - if (retval) { - free(scratch.data); - return(retval); - } - - /* Decrypt data */ - retval = krb5_decrypt(context, (char *) data->contents + 4, - (krb5_pointer) scratch.data, - scratch.length - 4, &eblock, 0); - if (retval) { - (void) krb5_finish_key(context, &eblock); - free(scratch.data); - return(retval); - } - - scratch.length = (((int) ((unsigned char *)data->contents)[0] << 24) - + ((int) ((unsigned char *)data->contents)[1] << 16) - + ((int) ((unsigned char *)data->contents)[2] << 8) - + (int) ((unsigned char *)data->contents)[3]); - free_scratch++; + if (etype_info) { + enctype = etype_info[0]->etype; + salt.data = etype_info[0]->salt; + salt.length = etype_info[0]->length; } else { - scratch.data = (char *) data->contents; - scratch.length = data->length; + enctype = request->ktype[0]; + if ((retval = krb5_principal2salt(context, request->client, &salt))) + return(retval); + f_salt = 1; } - - retval = (*p_system->verify)(context, client, src_addr, &scratch); - if (free_scratch) - free(scratch.data); - if (retval) - return retval; - if (flags) - *flags = p_system->flags; - - /* Generate a request id by crc32ing the (encrypted) preauth data. */ - /* Note: The idea behind req_id is that it is dependant upon - the information in data. This could then be used for - replay detection. */ - /* MUST malloc cksum.contents */ - cksum.contents = (krb5_octet *)calloc(1, - krb5_checksum_size(context, CKSUMTYPE_CRC32)); - if (!cksum.contents) return(1); - - if (krb5_calculate_checksum(context, CKSUMTYPE_CRC32, - data->contents, - data->length, - 0, /* seed is ignored */ - 0, /* seed length is ignored */ - &cksum )) { - *req_id = 0; - } else { - /* Checksum length should be 32 bits, so truncation should never - take place */ - if ( cksum.length > sizeof(*req_id)) cksum.length = sizeof(*req_id); - - /* Offset req_id for 64 bit systems */ - memcpy((char *)req_id + (sizeof(*req_id) - cksum.length), - cksum.contents,cksum.length); - } - free(cksum.contents); - return(0); -} - -static krb5_error_code -find_preauthenticator(type, preauth) - int type; - krb5_preauth_ops **preauth; -{ - krb5_preauth_ops *ap = preauth_systems; - while ((ap->type != -1) && (ap->type != type)) - ap++; - if (ap->type == -1) - return(KRB5_PREAUTH_BAD_TYPE); - *preauth = ap; - return 0; -} - -/* - * Format is: 8 bytes of random confounder, - * 1 byte version number (currently 0), - * 4 bytes: number of seconds since Jan 1, 1970, in MSB order. - */ -int seeded = 0 ; /* Used by srand below */ + if ((retval = (*key_proc)(context, enctype, &salt, key_seed, + &def_enc_key))) + goto cleanup; + -krb5_error_code -get_unixtime_padata(context, client, src_addr, pa_data) - krb5_context context; - krb5_principal client; - krb5_address **src_addr; - krb5_pa_data *pa_data; -{ - unsigned char *tmp; - krb5_error_code retval; - krb5_timestamp kdc_time; - int i; + for (pa = preauth_to_use; *pa; pa++) { + if (find_pa_system((*pa)->pa_type, &ops)) + continue; - pa_data->length = 13; - tmp = pa_data->contents = (unsigned char *) malloc(pa_data->length); - if (!tmp) - return(ENOMEM); + if (ops->obtain == 0) + continue; + + retval = ((ops)->obtain)(context, *pa, etype_info, def_enc_key, + key_proc, key_seed, creds, + request, send_pa); + if (retval) + goto cleanup; - retval = krb5_timeofday(context, &kdc_time); - if (retval) - return retval; - if ( !seeded) { - seeded = (int) kdc_time + getpid(); - srand(seeded); + if (*send_pa) + send_pa++; + *send_pa = 0; } - for (i=0; i < 8; i++) - *tmp++ = rand() & 255; + retval = 0; - *tmp++ = (unsigned char) 0; - *tmp++ = (unsigned char) ((kdc_time >> 24) & 255); - *tmp++ = (unsigned char) ((kdc_time >> 16) & 255); - *tmp++ = (unsigned char) ((kdc_time >> 8) & 255); - *tmp++ = (unsigned char) (kdc_time & 255); + if (send_pa_list[0]) { + request->padata = send_pa_list; + send_pa_list = 0; + } - return(0); +cleanup: + if (f_salt) + krb5_xfree(salt.data); + if (send_pa_list) + krb5_free_pa_data(context, send_pa_list); + if (def_enc_key) + krb5_free_keyblock(context, def_enc_key); + return retval; + } krb5_error_code -verify_unixtime_padata(context, client, src_addr, data) - krb5_context context; - krb5_principal client; - krb5_address **src_addr; - krb5_data *data; +krb5_process_padata(context, request, as_reply, key_proc, keyseed, + creds, do_more) + krb5_context context; + krb5_kdc_req * request; + krb5_kdc_rep * as_reply; + git_key_proc key_proc; + krb5_const_pointer keyseed; + krb5_creds * creds; + krb5_int32 * do_more; +{ + *do_more = 0; + return 0; +} + +static krb5_error_code +obtain_enc_ts_padata(context, in_padata, etype_info, def_enc_key, + key_proc, key_seed, creds, request, out_padata) + krb5_context context; + krb5_pa_data * in_padata; + krb5_etype_info etype_info; + krb5_keyblock * def_enc_key; + git_key_proc key_proc; + krb5_const_pointer key_seed; + krb5_creds * creds; + krb5_kdc_req * request; + krb5_pa_data ** out_padata; { - unsigned char *tmp; - krb5_error_code retval; - krb5_timestamp currenttime, patime; - extern krb5_deltat krb5_clockskew; -#define in_clock_skew(date) (labs((date)-currenttime) < krb5_clockskew) - - tmp = (unsigned char *) data->data; - if (tmp[8] != 0) - return KRB5_PREAUTH_FAILED; - patime = (int) tmp[9] << 24; - patime += (int) tmp[10] << 16; - patime += (int) tmp[11] << 8; - patime += tmp[12]; - - retval = krb5_timeofday(context, ¤ttime); + krb5_pa_enc_ts pa_enc; + krb5_error_code retval; + krb5_data * scratch; + krb5_enc_data enc_data; + krb5_pa_data * pa; + + + enc_data.ciphertext.data = 0; + + retval = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec); if (retval) - return retval; + return retval; - if (!in_clock_skew(patime)) - return KRB5_PREAUTH_FAILED; + if ((retval = encode_krb5_pa_enc_ts(&pa_enc, &scratch)) != 0) + return retval; - return 0; -} + if ((retval = krb5_encrypt_data(context, def_enc_key, 0, scratch, + &enc_data))) + goto cleanup; -#ifdef KRBCONF_SECUREID -#include "sdcli.h" -#include "sdconf.c" + krb5_free_data(context, scratch); + scratch = 0; + + if ((retval = encode_krb5_enc_data(&enc_data, &scratch)) != 0) + goto cleanup; -krb5_error_code -verify_securid_padata(client, src_addr, data) - krb5_principal client; - krb5_address **src_addr; - krb5_data *data; -{ - extern perform_hw; - - if (perform_hw) { - krb5_error_code retval; - char username[255]; - struct SD_CLIENT sd; - - memset((char *)&sd,0, sizeof (sd)); - memset((char *) username, 0, sizeof(username)); - memcpy((char *) username, krb5_princ_component(context, client,0)->data, - krb5_princ_component(context, client,0)->length); - /* If Instance then Append */ - if (krb5_princ_size(context, client) > 1 ) { - if (strncmp(krb5_princ_realm(context, client)->data, - krb5_princ_component(context, client,1)->data, - krb5_princ_component(context, client,1)->length) || - krb5_princ_realm(context, client)->length != - krb5_princ_component(context, client,1)->length) { - strncat(username,"/",1); - strncat(username,krb5_princ_component(context, client,1)->data, - krb5_princ_component(context, client,1)->length); - } - } - if (retval = sd_check(data->data,username,&sd) != ACM_OK) { - syslog(LOG_INFO, - "%s - Invalid Securid Authentication Data sd_check Code %d", - username, retval); - return(KRB5_PREAUTH_FAILED); - } - return(0); - } else { - char *username = 0; - - krb5_unparse_name(context, client,&username); - syslog(LOG_INFO, - "%s Provided Securid but this KDC does not support Securid", - username); - free(username); - return(KRB5_PREAUTH_FAILED); + if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) { + retval = ENOMEM; + goto cleanup; } -} -#else -krb5_error_code -verify_securid_padata(context, client, src_addr, data) - krb5_context context; - krb5_principal client; - krb5_address **src_addr; - krb5_data *data; -{ - char *username = 0; - krb5_unparse_name(context, client,&username); - syslog(LOG_INFO, - "%s Provided Securid but this KDC does not support Securid", - username); - free(username); - return(KRB5_PREAUTH_FAILED); -} -#endif + pa->magic = KV5M_PA_DATA; + pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP; + pa->length = scratch->length; + pa->contents = scratch->data; + *out_padata = pa; -/* -static char *krb5_SecureId_prompt = "\nEnter Your SecurId Access Code Prepended with Your PIN\n (or a \'#\'if Your PIN is entered on the card keypad)\n or Type return if You Do NOT Use a SecurId Card: "; - */ -static char *krb5_SecureId_prompt = "\nEnter Your SecurId Access Code Prepended with Your PIN\n (or a \'#\'if Your PIN is entered on the card keypad): "; + krb5_xfree(scratch); + scratch = 0; -krb5_error_code -get_securid_padata(context, client,src_addr,pa_data) - krb5_context context; - krb5_principal client; - krb5_address **src_addr; - krb5_pa_data *pa_data; + retval = 0; + +cleanup: + if (scratch) + krb5_free_data(context, scratch); + if (enc_data.ciphertext.data) + krb5_xfree(enc_data.ciphertext.data); + return retval; +} + +static krb5_error_code +find_pa_system(type, preauth) + int type; + krb5_preauth_ops **preauth; { + krb5_preauth_ops *ap = preauth_systems; + + while ((ap->type != -1) && (ap->type != type)) + ap++; + if (ap->type == -1) + return(KRB5_PREAUTH_BAD_TYPE); + *preauth = ap; + return 0; +} - char temp[MAX_PREAUTH_SIZE]; - int tempsize; - int retval = 0; - - tempsize = sizeof(temp) - 1; - if (krb5_read_password(context, krb5_SecureId_prompt, 0, temp, &tempsize)) - return(KRB5_PARSE_ILLCHAR); - temp[tempsize] = '\0'; - - if (temp[0] == '\0') - return(KRB5_PARSE_ILLCHAR); - pa_data->length = strlen(temp) + 1; - pa_data->contents = (krb5_octet *) calloc(1,pa_data->length); - if (pa_data->contents) { - memcpy(pa_data->contents,temp,pa_data->length); - retval = 0; - } - else retval = ENOMEM; - memset(temp,0,pa_data->length); - return(retval); -} -- cgit v1.1