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 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