Home | History | Annotate | Line # | Download | only in doc
      1 Currently, getting an initial ticket for a user involves many function
      2 calls, especially when a full set of features including password
      3 expiration and challenge preauthentication is desired.  In order to
      4 solve this problem, a new api is proposed.
      5 
      6 typedef struct _krb5_prompt {
      7     char *prompt;
      8     int hidden;
      9     krb5_data *reply;
     10 } krb5_prompt;
     11 
     12 typedef int (*krb5_prompter_fct)(krb5_context context,
     13 				 void *data,
     14 				 const char *banner,
     15 				 int num_prompts,
     16 				 krb5_prompt prompts[]);
     17 
     18 typedef 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 
     43 void krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt);
     44 
     45 void krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt,
     46 					  krb5_deltat tkt_life);
     47 void krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt,
     48 					    krb5_deltat renew_life);
     49 void krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt,
     50 					     int forwardable);
     51 void krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt,
     52 					   int proxiable);
     53 void krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt,
     54 					    krb5_enctype *etype_list,
     55 					    int etype_list_length);
     56 void krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt,
     57 					      krb5_address **addresses);
     58 void krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt,
     59 					      krb5_preauthtype *preauth_list,
     60 					      int preauth_list_length);
     61 void krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt,
     62 				      krb5_data *salt);
     63 
     64 krb5_error_code
     65 krb5_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 
     75 This function will attempt to acquire an initial ticket.  The function
     76 will perform whatever tasks are necessary to do so.  This may include
     77 changing an expired password, preauthentication.
     78 
     79 The arguments divide into two types.  Some arguments are basically
     80 invariant and arbitrary across all initial tickets, and if not
     81 specified are determined by configuration or library defaults.  Some
     82 arguments are different for each execution or application, and if not
     83 specified can be determined correctly from system configuration or
     84 environment.  The former arguments are contained in a structure whose
     85 pointer is passed to the function.  A bitmask specifies which elements
     86 of the structure should be used.  In most cases, a NULL pointer can be
     87 used.  The latter arguments are specified as individual arguments to
     88 the function.
     89 
     90 If a pointer to a credential is specified, the initial credential is
     91 filled in.  If the caller only wishes to do a simple password check
     92 and will not be doing any other kerberos functions, then a NULL
     93 pointer may be specified, and the credential will be destroyed.
     94 
     95 If the client name is non-NULL, the initial ticket requested will be
     96 for that principal.  Otherwise, the principal will be the username
     97 specified by the USER environment variable, or if the USER environment
     98 variable is not set, the username corresponding to the real user id of
     99 the caller.
    100 
    101 If the password is non-NULL, then this string is used as the password.
    102 Otherwise, the prompter function will be used to prompt the user for
    103 the password.
    104 
    105 If a prompter function is non-NULL, it will be used if additional user
    106 input is required, such as if the user's password has expired and
    107 needs to be changed, or if input preauthentication is necessary.  If
    108 no function is specified and input is required, then the login will
    109 fail.
    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 
    145 If the start_time is zero, then the requested ticket will be valid
    146 beginning immediately.  Otherwise, the start_time indicates how far in
    147 the future the ticket should be postdated.
    148 
    149 If the in_tkt_service name is non-NULL, that principal name will be
    150 used as the server name for the initial ticket request.  The realm of
    151 the name specified will be ignored and will be set to the realm of the
    152 client name.  If no in_tkt_service name is specified,
    153 krbtgt/CLIENT-REALM@CLIENT-REALM will be used.
    154 
    155 For the rest of arguments, a configuration or library default will be
    156 used if no value is specified in the options structure.
    157 
    158 If a tkt_life is specified, that will be the lifetime of the ticket.
    159 The library default is 10 hours; there is no configuration variable
    160 (there should be, but it's not there now).
    161 
    162 If a renew_life is specified and non-zero, then the RENEWABLE option
    163 on the ticket will be set, and the value of the argument will be the
    164 the renewable lifetime.  The configuration variable [libdefaults]
    165 "renew_lifetime" is the renewable lifetime if none is passed in.  The
    166 library default is not to set the RENEWABLE option.
    167 
    168 If forwardable is specified, the FORWARDABLE option on the ticket will
    169 be set if and only if forwardable is non-zero.  The configuration
    170 variable [libdefaults] "forwardable" is used if no value is passed in.
    171 The 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
    173 not to set the FORWARDABLE option.
    174 
    175 If proxiable is specified, the PROXIABLE option on the ticket will be
    176 set if and only if proxiable is non-zero.  The configuration variable
    177 [libdefaults] "proxiable" is used if no value is passed in.  The
    178 option 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
    180 set the PROXIABLE option.
    181 
    182 If etype_list is specified, it will be used as the list of desired
    183 encryption algorithms in the request.  The configuration variable
    184 [libdefaults] "default_tkt_enctypes" is used if no value is passed in.
    185 The library default is "des-cbc-md5 des-cbc-crc".
    186 
    187 If address_list is specified, it will be used as the list of addresses
    188 for which the ticket will be valid.  The library default is to use all
    189 local non-loopback addresses.  There is no configuration variable.
    190 
    191 If preauth_list is specified, it names preauth data types which will
    192 be included in the request.  The library default is to interact with
    193 the kdc to determine the required preauth types.  There is no
    194 configuration variable.
    195 
    196 If salt is specified, it specifies the salt which will be used when
    197 converting the password to a key.  The library default is to interact
    198 with the kdc to determine the correct salt.  There is no configuration
    199 variable.
    200 
    201 ================================================================
    202 
    203 typedef 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 
    210 void krb5_verify_init_creds_opt_init(krb5_init_creds_opt *options);
    211 void krb5_verify_init_creds_opt_set_ap_req_nofail(krb5_init_creds_opt *options,
    212 						  int ap_req_nofail);
    213 
    214 krb5_error_code
    215 krb5_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 
    222 This function will use the initial ticket in creds to make an AP_REQ
    223 and verify it to insure that the AS_REP has not been spoofed.
    224 
    225 If the ap_req_server name is non-NULL, then this service name will be
    226 used for the AP_REQ; otherwise, the default host key
    227 (host/hostname.domain@LOCAL-REALM) will be used.
    228 
    229 If ap_req_keytab is non-NULL, the service key for the verification
    230 will be read from that keytab; otherwise, the service key will be read
    231 from the default keytab. 
    232 
    233 If the service of the ticket in creds is the same as the service name
    234 for the AP_REQ, then this ticket will be used directly.  If the ticket
    235 is a tgt, then it will be used to obtain credentials for the service.
    236 Otherwise, the verification will fail, and return an error.
    237 
    238 Other failures of the AP_REQ verification may or may not be considered
    239 errors, as described below.
    240 
    241 If a pointer to a credential cache handle is specified, and the handle
    242 is NULL, a credential cache handle referring to all credentials
    243 obtained in the course of verifying the user will be returned.  In
    244 order to avoid potential setuid race conditions and other problems
    245 related to file system access, this handle will refer to a memory
    246 credential cache.  If the handle is non-NULL, then the credentials
    247 will be added to the existing ccache.  If the caller only wishes to
    248 verify the password and will not be doing any other kerberos
    249 functions, then a NULL pointer may be specified, and the credentials
    250 will be deleted before the function returns.
    251 
    252 If ap_req_nofail is specified, then failures of the AP_REQ
    253 verification are considered errors if and only if ap_req_nofail is
    254 non-zero.
    255 
    256 Whether or not AP_REQ validation is performed and what failures mean
    257 depends 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
    262 can be decrypted and verified.
    263 
    264  C) The administrator has specified in a configuration file that
    265 AP_REQ validation must succeed.  This is basically a paranoid bit, and
    266 can be overridden by the application based on a command line flag or
    267 other application-specific info.  This flag is especially useful if
    268 the admin is concerned that DNS might be spoofed while determining the
    269 host/FQDN name.  The configuration variable [libdefaults]
    270 "verify_ap_req_nofail" is used if no value is passed in.  The library
    271 default is not to set this option.
    272 
    273 Initial ticket verification will succeed if and only if:
    274 
    275  - A && B    or
    276  - !A && !C
    277 
    278 ================================================================
    279 
    280 For illustrative purposes, here's the invocations I expect some
    281 programs will use.  Of course, error checking needs to be added.
    282 
    283 kinit:
    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 
    295 login:
    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 
    307 xdm:
    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 
    319 passwd:
    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 
    329 pop3d (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 
    338 password expiration has a subtlety.  When a password expires and is
    339 changed, there is a delay between when the master gets the new key
    340 (immediately), and the slaves (propogation interval).  So, when
    341 getting an in_tkt, if the password is expired, the request should be
    342 reissued to the master (this kind of sucks if you have SAM, oh well).
    343 If this says expired, too, then the password should be changed, and
    344 then the initial ticket request should be issued to the master again.
    345 If the master times out, then a message that the password has expired
    346 and cannot be changed due to the master being unreachable should be
    347 displayed.
    348 
    349 ================================================================
    350 
    351 get_init_creds reads config stuff from:
    352 
    353 [libdefaults]
    354 	varname1 = defvalue
    355 	REALM = {
    356 		varname1 = value
    357 		varname2 = value
    358 	}
    359 
    360 typedef 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