1Currently, getting an initial ticket for a user involves many function 2calls, especially when a full set of features including password 3expiration and challenge preauthentication is desired. In order to 4solve this problem, a new api is proposed. 5 6typedef struct _krb5_prompt { 7 char *prompt; 8 int hidden; 9 krb5_data *reply; 10} krb5_prompt; 11 12typedef int (*krb5_prompter_fct)(krb5_context context, 13 void *data, 14 const char *banner, 15 int num_prompts, 16 krb5_prompt prompts[]); 17 18typedef struct _krb5_get_init_creds_opt { 19 krb5_flags flags; 20 krb5_deltat tkt_life; 21 krb5_deltat renew_life; 22 int forwardable; 23 int proxiable; 24 krb5_enctype *etype_list; 25 int etype_list_length; 26 krb5_address **address_list; 27 /* XXX the next three should not be used, as they may be 28 removed later */ 29 krb5_preauthtype *preauth_list; 30 int preauth_list_length; 31 krb5_data *salt; 32} krb5_get_init_creds_opt; 33 34#define KRB5_GET_INIT_CREDS_OPT_TKT_LIFE 0x0001 35#define KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE 0x0002 36#define KRB5_GET_INIT_CREDS_OPT_FORWARDABLE 0x0004 37#define KRB5_GET_INIT_CREDS_OPT_PROXIABLE 0x0008 38#define KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST 0x0010 39#define KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST 0x0020 40#define KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST 0x0040 41#define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080 42 43void krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt); 44 45void krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt, 46 krb5_deltat tkt_life); 47void krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt, 48 krb5_deltat renew_life); 49void krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt, 50 int forwardable); 51void krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt, 52 int proxiable); 53void krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, 54 krb5_enctype *etype_list, 55 int etype_list_length); 56void krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt, 57 krb5_address **addresses); 58void krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt, 59 krb5_preauthtype *preauth_list, 60 int preauth_list_length); 61void krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, 62 krb5_data *salt); 63 64krb5_error_code 65krb5_get_init_creds_password(krb5_context context, 66 krb5_creds *creds, 67 krb5_principal client, 68 char *password, 69 krb5_prompter_fct prompter, 70 void *data, 71 krb5_deltat start_time, 72 char *in_tkt_service, 73 krb5_get_init_creds_opt *options); 74 75This function will attempt to acquire an initial ticket. The function 76will perform whatever tasks are necessary to do so. This may include 77changing an expired password, preauthentication. 78 79The arguments divide into two types. Some arguments are basically 80invariant and arbitrary across all initial tickets, and if not 81specified are determined by configuration or library defaults. Some 82arguments are different for each execution or application, and if not 83specified can be determined correctly from system configuration or 84environment. The former arguments are contained in a structure whose 85pointer is passed to the function. A bitmask specifies which elements 86of the structure should be used. In most cases, a NULL pointer can be 87used. The latter arguments are specified as individual arguments to 88the function. 89 90If a pointer to a credential is specified, the initial credential is 91filled in. If the caller only wishes to do a simple password check 92and will not be doing any other kerberos functions, then a NULL 93pointer may be specified, and the credential will be destroyed. 94 95If the client name is non-NULL, the initial ticket requested will be 96for that principal. Otherwise, the principal will be the the username 97specified by the USER environment variable, or if the USER environment 98variable is not set, the username corresponding to the real user id of 99the caller. 100 101If the password is non-NULL, then this string is used as the password. 102Otherwise, the prompter function will be used to prompt the user for 103the password. 104 105If a prompter function is non-NULL, it will be used if additional user 106input is required, such as if the user's password has expired and 107needs to be changed, or if input preauthentication is necessary. If 108no function is specified and input is required, then the login will 109fail. 110 111 The context argument is the same as that passed to krb5_login. 112 The data argument is passed unmodified to the prompter 113 function and is intended to be used to pass application data 114 (such as a display handle) to the prompter function. 115 116 The banner argument, if non-NULL, will indicate what sort of 117 input is expected from the user (for example, "Password has 118 expired and must be changed" or "Enter Activcard response for 119 challenge 012345678"), and should be displayed accordingly. 120 121 The num_prompts argument indicates the number of values which 122 should be prompted for. If num_prompts == 0, then the banner 123 contains an informational message which should be displayed to 124 the user. 125 126 The prompts argument contains an array describing the values 127 for which the user should be prompted. The prompt member 128 indicates the prompt for each value ("Enter new 129 password"/"Enter it again", or "Challenge response"). The 130 hidden member is nonzero if the response should not be 131 displayed back to the user. The reply member is a pointer to 132 krb5_data structure which has already been allocated. The 133 prompter should fill in the structure with the NUL-terminated 134 response from the user. 135 136 If the response data does not fit, or if any other error 137 occurs, then the prompter function should return a non-zero 138 value which will be returned by the krb5_get_init_creds 139 function. Otherwise, zero should be returned. 140 141 The library function krb5_prompter_posix() implements 142 a prompter using a posix terminal for user in. This function 143 does not use the data argument. 144 145If the start_time is zero, then the requested ticket will be valid 146beginning immediately. Otherwise, the start_time indicates how far in 147the future the ticket should be postdated. 148 149If the in_tkt_service name is non-NULL, that principal name will be 150used as the server name for the initial ticket request. The realm of 151the name specified will be ignored and will be set to the realm of the 152client name. If no in_tkt_service name is specified, 153krbtgt/CLIENT-REALM@CLIENT-REALM will be used. 154 155For the rest of arguments, a configuration or library default will be 156used if no value is specified in the options structure. 157 158If a tkt_life is specified, that will be the lifetime of the ticket. 159The library default is 10 hours; there is no configuration variable 160(there should be, but it's not there now). 161 162If a renew_life is specified and non-zero, then the RENEWABLE option 163on the ticket will be set, and the value of the argument will be the 164the renewable lifetime. The configuration variable [libdefaults] 165"renew_lifetime" is the renewable lifetime if none is passed in. The 166library default is not to set the RENEWABLE option. 167 168If forwardable is specified, the FORWARDABLE option on the ticket will 169be set if and only if forwardable is non-zero. The configuration 170variable [libdefaults] "forwardable" is used if no value is passed in. 171The option will be set if and only if the variable is "y", "yes", 172"true", "t", "1", or "on", case insensitive. The library default is 173not to set the FORWARDABLE option. 174 175If proxiable is specified, the PROXIABLE option on the ticket will be 176set if and only if proxiable is non-zero. The configuration variable 177[libdefaults] "proxiable" is used if no value is passed in. The 178option will be set if and only if the variable is "y", "yes", "true", 179"t", "1", or "on", case insensitive. The library default is not to 180set the PROXIABLE option. 181 182If etype_list is specified, it will be used as the list of desired 183encryption algorithms in the request. The configuration variable 184[libdefaults] "default_tkt_enctypes" is used if no value is passed in. 185The library default is "des-cbc-md5 des-cbc-crc". 186 187If address_list is specified, it will be used as the list of addresses 188for which the ticket will be valid. The library default is to use all 189local non-loopback addresses. There is no configuration variable. 190 191If preauth_list is specified, it names preauth data types which will 192be included in the request. The library default is to interact with 193the kdc to determine the required preauth types. There is no 194configuration variable. 195 196If salt is specified, it specifies the salt which will be used when 197converting the password to a key. The library default is to interact 198with the kdc to determine the correct salt. There is no configuration 199variable. 200 201================================================================ 202 203typedef struct _krb5_verify_init_creds_opt { 204 krb5_flags flags; 205 int ap_req_nofail; 206} krb5_verify_init_creds_opt; 207 208#define KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL 0x0001 209 210void krb5_verify_init_creds_opt_init(krb5_init_creds_opt *options); 211void krb5_verify_init_creds_opt_set_ap_req_nofail(krb5_init_creds_opt *options, 212 int ap_req_nofail); 213 214krb5_error_code 215krb5_verify_init_creds(krb5_context context, 216 krb5_creds *creds, 217 krb5_principal ap_req_server, 218 krb5_keytab ap_req_keytab, 219 krb5_ccache *ccache, 220 krb5_verify_init_creds_opt *options); 221 222This function will use the initial ticket in creds to make an AP_REQ 223and verify it to insure that the AS_REP has not been spoofed. 224 225If the ap_req_server name is non-NULL, then this service name will be 226used for the AP_REQ; otherwise, the default host key 227(host/hostname.domain@LOCAL-REALM) will be used. 228 229If ap_req_keytab is non-NULL, the service key for the verification 230will be read from that keytab; otherwise, the service key will be read 231from the default keytab. 232 233If the service of the ticket in creds is the same as the service name 234for the AP_REQ, then this ticket will be used directly. If the ticket 235is a tgt, then it will be used to obtain credentials for the service. 236Otherwise, the verification will fail, and return an error. 237 238Other failures of the AP_REQ verification may or may not be considered 239errors, as described below. 240 241If a pointer to a credential cache handle is specified, and the handle 242is NULL, a credential cache handle referring to all credentials 243obtained in the course of verifying the user will be returned. In 244order to avoid potential setuid race conditions and other problems 245related to file system access, this handle will refer to a memory 246credential cache. If the handle is non-NULL, then the credentials 247will be added to the existing ccache. If the caller only wishes to 248verify the password and will not be doing any other kerberos 249functions, then a NULL pointer may be specified, and the credentials 250will be deleted before the function returns. 251 252If ap_req_nofail is specified, then failures of the AP_REQ 253verification are considered errors if and only if ap_req_nofail is 254non-zero. 255 256Whether or not AP_REQ validation is performed and what failures mean 257depends on these inputs: 258 259 A) The appropriate keytab exists and contains the named key. 260 261 B) An AP_REQ request to the kdc succeeds, and the resulting AP_REQ 262can be decrypted and verified. 263 264 C) The administrator has specified in a configuration file that 265AP_REQ validation must succeed. This is basically a paranoid bit, and 266can be overridden by the application based on a command line flag or 267other application-specific info. This flag is especially useful if 268the admin is concerned that DNS might be spoofed while determining the 269host/FQDN name. The configuration variable [libdefaults] 270"verify_ap_req_nofail" is used if no value is passed in. The library 271default is not to set this option. 272 273Initial ticket verification will succeed if and only if: 274 275 - A && B or 276 - !A && !C 277 278================================================================ 279 280For illustrative purposes, here's the invocations I expect some 281programs will use. Of course, error checking needs to be added. 282 283kinit: 284 285 /* Fill in client from the command line || existing ccache, and, 286 start_time, and options.{tkt_life,renew_life,forwardable,proxiable} 287 from the command line. Some or all may remain unset. */ 288 289 krb5_get_init_creds(context, &creds, client, 290 krb5_initial_prompter_posix, NULL, 291 start_time, NULL, &options); 292 krb5_cc_store_cred(context, ccache, &creds); 293 krb5_free_cred_contents(context, &creds); 294 295login: 296 297 krb5_get_init_creds(context, &creds, client, 298 krb5_initial_prompter_posix, NULL, 299 0, NULL, NULL); 300 krb5_verify_init_creds(context, &creds, NULL, NULL, &vcc, NULL); 301 /* setuid */ 302 krb5_cc_store_cred(context, ccache, &creds); 303 krb5_cc_copy(context, vcc, ccache); 304 krb5_free_cred_contents(context, &creds); 305 krb5_cc_destroy(context, vcc); 306 307xdm: 308 309 krb5_get_initial_creds(context, &creds, client, 310 krb5_initial_prompter_xt, (void *) &xtstuff, 311 0, NULL, NULL); 312 krb5_verify_init_creds(context, &creds, NULL, NULL, &vcc, NULL); 313 /* setuid */ 314 krb5_cc_store_cred(context, ccache, &creds); 315 krb5_free_cred_contents(context, &creds); 316 krb5_cc_copy(context, vcc, ccache); 317 krb5_cc_destroy(context, vcc); 318 319passwd: 320 321 krb5_init_creds_opt_init(&options); 322 krb5_init_creds_opt_set_tkt_life = 300; 323 krb5_get_initial_creds(context, &creds, client, 324 krb5_initial_prompter_posix, NULL, 325 0, "kadmin/changepw", &options); 326 /* change password */ 327 krb5_free_cred_contents(context, &creds); 328 329pop3d (simple password validator when no user interation possible): 330 331 krb5_get_initial_creds(context, &creds, client, 332 NULL, NULL, 0, NULL, NULL); 333 krb5_verify_init_creds(context, &creds, NULL, NULL, &vcc, NULL); 334 krb5_cc_destroy(context, vcc); 335 336================================================================ 337 338password expiration has a subtlety. When a password expires and is 339changed, there is a delay between when the master gets the new key 340(immediately), and the slaves (propogation interval). So, when 341getting an in_tkt, if the password is expired, the request should be 342reissued to the master (this kind of sucks if you have SAM, oh well). 343If this says expired, too, then the password should be changed, and 344then the initial ticket request should be issued to the master again. 345If the master times out, then a message that the password has expired 346and cannot be changed due to the master being unreachable should be 347displayed. 348 349================================================================ 350 351get_init_creds reads config stuff from: 352 353[libdefaults] 354 varname1 = defvalue 355 REALM = { 356 varname1 = value 357 varname2 = value 358 } 359 360typedef struct _krb5_get_init_creds_opt { 361 krb5_flags flags; 362 krb5_deltat tkt_life; /* varname = "ticket_lifetime" */ 363 krb5_deltat renew_life; /* varname = "renew_lifetime" */ 364 int forwardable; /* varname = "forwardable" */ 365 int proxiable; /* varname = "proxiable" */ 366 krb5_enctype *etype_list; /* varname = "default_tkt_enctypes" */ 367 int etype_list_length; 368 krb5_address **address_list; /* no varname */ 369 krb5_preauthtype *preauth_list; /* no varname */ 370 int preauth_list_length; 371 krb5_data *salt; 372} krb5_get_init_creds_opt; 373 374 375