155682SmarkmCurrently, getting an initial ticket for a user involves many function
255682Smarkmcalls, especially when a full set of features including password
355682Smarkmexpiration and challenge preauthentication is desired.  In order to
455682Smarkmsolve this problem, a new api is proposed.
555682Smarkm
655682Smarkmtypedef struct _krb5_prompt {
755682Smarkm    char *prompt;
855682Smarkm    int hidden;
955682Smarkm    krb5_data *reply;
1055682Smarkm} krb5_prompt;
1155682Smarkm
1255682Smarkmtypedef int (*krb5_prompter_fct)(krb5_context context,
1355682Smarkm				 void *data,
1455682Smarkm				 const char *banner,
1555682Smarkm				 int num_prompts,
1655682Smarkm				 krb5_prompt prompts[]);
1755682Smarkm
1855682Smarkmtypedef struct _krb5_get_init_creds_opt {
1955682Smarkm    krb5_flags flags;
2055682Smarkm    krb5_deltat tkt_life;
2155682Smarkm    krb5_deltat renew_life;
2255682Smarkm    int forwardable;
2355682Smarkm    int proxiable;
2455682Smarkm    krb5_enctype *etype_list;
2555682Smarkm    int etype_list_length;
2655682Smarkm    krb5_address **address_list;
2755682Smarkm	/* XXX the next three should not be used, as they may be
2855682Smarkm	removed later */
2955682Smarkm    krb5_preauthtype *preauth_list;
3055682Smarkm    int preauth_list_length;
3155682Smarkm    krb5_data *salt;
3255682Smarkm} krb5_get_init_creds_opt;
3355682Smarkm
3455682Smarkm#define KRB5_GET_INIT_CREDS_OPT_TKT_LIFE	0x0001
3555682Smarkm#define KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE	0x0002
3655682Smarkm#define KRB5_GET_INIT_CREDS_OPT_FORWARDABLE	0x0004
3755682Smarkm#define KRB5_GET_INIT_CREDS_OPT_PROXIABLE	0x0008
3855682Smarkm#define KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST	0x0010
3955682Smarkm#define KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST	0x0020
4055682Smarkm#define KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST	0x0040
4155682Smarkm#define KRB5_GET_INIT_CREDS_OPT_SALT		0x0080
4255682Smarkm
4355682Smarkmvoid krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt);
4455682Smarkm
4555682Smarkmvoid krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt,
4655682Smarkm					  krb5_deltat tkt_life);
4755682Smarkmvoid krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt,
4855682Smarkm					    krb5_deltat renew_life);
4955682Smarkmvoid krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt,
5055682Smarkm					     int forwardable);
5155682Smarkmvoid krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt,
5255682Smarkm					   int proxiable);
5355682Smarkmvoid krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt,
5455682Smarkm					    krb5_enctype *etype_list,
5555682Smarkm					    int etype_list_length);
5655682Smarkmvoid krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt,
5755682Smarkm					      krb5_address **addresses);
5855682Smarkmvoid krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt,
5955682Smarkm					      krb5_preauthtype *preauth_list,
6055682Smarkm					      int preauth_list_length);
6155682Smarkmvoid krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt,
6255682Smarkm				      krb5_data *salt);
6355682Smarkm
6455682Smarkmkrb5_error_code
6555682Smarkmkrb5_get_init_creds_password(krb5_context context,
6655682Smarkm			     krb5_creds *creds,
6755682Smarkm			     krb5_principal client,
6855682Smarkm			     char *password,
6955682Smarkm			     krb5_prompter_fct prompter,
7055682Smarkm			     void *data,
7155682Smarkm			     krb5_deltat start_time,
7255682Smarkm			     char *in_tkt_service,
7355682Smarkm			     krb5_get_init_creds_opt *options);
7455682Smarkm
7555682SmarkmThis function will attempt to acquire an initial ticket.  The function
7655682Smarkmwill perform whatever tasks are necessary to do so.  This may include
7755682Smarkmchanging an expired password, preauthentication.
7855682Smarkm
7955682SmarkmThe arguments divide into two types.  Some arguments are basically
8055682Smarkminvariant and arbitrary across all initial tickets, and if not
8155682Smarkmspecified are determined by configuration or library defaults.  Some
8255682Smarkmarguments are different for each execution or application, and if not
8355682Smarkmspecified can be determined correctly from system configuration or
8455682Smarkmenvironment.  The former arguments are contained in a structure whose
8555682Smarkmpointer is passed to the function.  A bitmask specifies which elements
8655682Smarkmof the structure should be used.  In most cases, a NULL pointer can be
8755682Smarkmused.  The latter arguments are specified as individual arguments to
8855682Smarkmthe function.
8955682Smarkm
9055682SmarkmIf a pointer to a credential is specified, the initial credential is
9155682Smarkmfilled in.  If the caller only wishes to do a simple password check
9255682Smarkmand will not be doing any other kerberos functions, then a NULL
9355682Smarkmpointer may be specified, and the credential will be destroyed.
9455682Smarkm
9555682SmarkmIf the client name is non-NULL, the initial ticket requested will be
96178825Sdfrfor that principal.  Otherwise, the principal will be the username
9755682Smarkmspecified by the USER environment variable, or if the USER environment
9855682Smarkmvariable is not set, the username corresponding to the real user id of
9955682Smarkmthe caller.
10055682Smarkm
10155682SmarkmIf the password is non-NULL, then this string is used as the password.
10255682SmarkmOtherwise, the prompter function will be used to prompt the user for
10355682Smarkmthe password.
10455682Smarkm
10555682SmarkmIf a prompter function is non-NULL, it will be used if additional user
10655682Smarkminput is required, such as if the user's password has expired and
10755682Smarkmneeds to be changed, or if input preauthentication is necessary.  If
10855682Smarkmno function is specified and input is required, then the login will
10955682Smarkmfail.
11055682Smarkm
11155682Smarkm	The context argument is the same as that passed to krb5_login.
11255682Smarkm	The data argument is passed unmodified to the prompter
11355682Smarkm	function and is intended to be used to pass application data
11455682Smarkm	(such as a display handle) to the prompter function.
11555682Smarkm
11655682Smarkm	The banner argument, if non-NULL, will indicate what sort of
11755682Smarkm	input is expected from the user (for example, "Password has
11855682Smarkm	expired and must be changed" or "Enter Activcard response for
11955682Smarkm	challenge 012345678"), and should be displayed accordingly.
12055682Smarkm	
12155682Smarkm	The num_prompts argument indicates the number of values which
12255682Smarkm	should be prompted for.  If num_prompts == 0, then the banner
12355682Smarkm	contains an informational message which should be displayed to
12455682Smarkm	the user.
12555682Smarkm
12655682Smarkm	The prompts argument contains an array describing the values
12755682Smarkm	for which the user should be prompted.  The prompt member
12855682Smarkm	indicates the prompt for each value ("Enter new
12955682Smarkm	password"/"Enter it again", or "Challenge response").  The
13055682Smarkm	hidden member is nonzero if the response should not be
13155682Smarkm	displayed back to the user.  The reply member is a pointer to
13255682Smarkm	krb5_data structure which has already been allocated.  The
13355682Smarkm	prompter should fill in the structure with the NUL-terminated
13455682Smarkm	response from the user.
13555682Smarkm
13655682Smarkm	If the response data does not fit, or if any other error
13755682Smarkm	occurs, then the prompter function should return a non-zero
13855682Smarkm	value which will be returned by the krb5_get_init_creds
13955682Smarkm	function. Otherwise, zero should be returned.
14055682Smarkm
14155682Smarkm	The library function krb5_prompter_posix() implements
14255682Smarkm	a prompter using a posix terminal for user in.  This function
14355682Smarkm	does not use the data argument.
14455682Smarkm
14555682SmarkmIf the start_time is zero, then the requested ticket will be valid
14655682Smarkmbeginning immediately.  Otherwise, the start_time indicates how far in
14755682Smarkmthe future the ticket should be postdated.
14855682Smarkm
14955682SmarkmIf the in_tkt_service name is non-NULL, that principal name will be
15055682Smarkmused as the server name for the initial ticket request.  The realm of
15155682Smarkmthe name specified will be ignored and will be set to the realm of the
15255682Smarkmclient name.  If no in_tkt_service name is specified,
15355682Smarkmkrbtgt/CLIENT-REALM@CLIENT-REALM will be used.
15455682Smarkm
15555682SmarkmFor the rest of arguments, a configuration or library default will be
15655682Smarkmused if no value is specified in the options structure.
15755682Smarkm
15855682SmarkmIf a tkt_life is specified, that will be the lifetime of the ticket.
15955682SmarkmThe library default is 10 hours; there is no configuration variable
16055682Smarkm(there should be, but it's not there now).
16155682Smarkm
16255682SmarkmIf a renew_life is specified and non-zero, then the RENEWABLE option
16355682Smarkmon the ticket will be set, and the value of the argument will be the
16455682Smarkmthe renewable lifetime.  The configuration variable [libdefaults]
16555682Smarkm"renew_lifetime" is the renewable lifetime if none is passed in.  The
16655682Smarkmlibrary default is not to set the RENEWABLE option.
16755682Smarkm
16855682SmarkmIf forwardable is specified, the FORWARDABLE option on the ticket will
16955682Smarkmbe set if and only if forwardable is non-zero.  The configuration
17055682Smarkmvariable [libdefaults] "forwardable" is used if no value is passed in.
17155682SmarkmThe option will be set if and only if the variable is "y", "yes",
17255682Smarkm"true", "t", "1", or "on", case insensitive.  The library default is
17355682Smarkmnot to set the FORWARDABLE option.
17455682Smarkm
17555682SmarkmIf proxiable is specified, the PROXIABLE option on the ticket will be
17655682Smarkmset if and only if proxiable is non-zero.  The configuration variable
17755682Smarkm[libdefaults] "proxiable" is used if no value is passed in.  The
17855682Smarkmoption will be set if and only if the variable is "y", "yes", "true",
17955682Smarkm"t", "1", or "on", case insensitive.  The library default is not to
18055682Smarkmset the PROXIABLE option.
18155682Smarkm
18255682SmarkmIf etype_list is specified, it will be used as the list of desired
18355682Smarkmencryption algorithms in the request.  The configuration variable
18455682Smarkm[libdefaults] "default_tkt_enctypes" is used if no value is passed in.
18555682SmarkmThe library default is "des-cbc-md5 des-cbc-crc".
18655682Smarkm
18755682SmarkmIf address_list is specified, it will be used as the list of addresses
18855682Smarkmfor which the ticket will be valid.  The library default is to use all
18955682Smarkmlocal non-loopback addresses.  There is no configuration variable.
19055682Smarkm
19155682SmarkmIf preauth_list is specified, it names preauth data types which will
19255682Smarkmbe included in the request.  The library default is to interact with
19355682Smarkmthe kdc to determine the required preauth types.  There is no
19455682Smarkmconfiguration variable.
19555682Smarkm
19655682SmarkmIf salt is specified, it specifies the salt which will be used when
19755682Smarkmconverting the password to a key.  The library default is to interact
19855682Smarkmwith the kdc to determine the correct salt.  There is no configuration
19955682Smarkmvariable.
20055682Smarkm
20155682Smarkm================================================================
20255682Smarkm
20355682Smarkmtypedef struct _krb5_verify_init_creds_opt {
20455682Smarkm    krb5_flags flags;
20555682Smarkm    int ap_req_nofail;
20655682Smarkm} krb5_verify_init_creds_opt;
20755682Smarkm
20855682Smarkm#define KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL	0x0001
20955682Smarkm
21055682Smarkmvoid krb5_verify_init_creds_opt_init(krb5_init_creds_opt *options);
21155682Smarkmvoid krb5_verify_init_creds_opt_set_ap_req_nofail(krb5_init_creds_opt *options,
21255682Smarkm						  int ap_req_nofail);
21355682Smarkm
21455682Smarkmkrb5_error_code
21555682Smarkmkrb5_verify_init_creds(krb5_context context,
21655682Smarkm		       krb5_creds *creds,
21755682Smarkm		       krb5_principal ap_req_server,
21855682Smarkm		       krb5_keytab ap_req_keytab,
21955682Smarkm		       krb5_ccache *ccache,
22055682Smarkm		       krb5_verify_init_creds_opt *options);
22155682Smarkm
22255682SmarkmThis function will use the initial ticket in creds to make an AP_REQ
22355682Smarkmand verify it to insure that the AS_REP has not been spoofed.
22455682Smarkm
22555682SmarkmIf the ap_req_server name is non-NULL, then this service name will be
22655682Smarkmused for the AP_REQ; otherwise, the default host key
22755682Smarkm(host/hostname.domain@LOCAL-REALM) will be used.
22855682Smarkm
22955682SmarkmIf ap_req_keytab is non-NULL, the service key for the verification
23055682Smarkmwill be read from that keytab; otherwise, the service key will be read
23155682Smarkmfrom the default keytab. 
23255682Smarkm
23355682SmarkmIf the service of the ticket in creds is the same as the service name
23455682Smarkmfor the AP_REQ, then this ticket will be used directly.  If the ticket
23555682Smarkmis a tgt, then it will be used to obtain credentials for the service.
23655682SmarkmOtherwise, the verification will fail, and return an error.
23755682Smarkm
23855682SmarkmOther failures of the AP_REQ verification may or may not be considered
23955682Smarkmerrors, as described below.
24055682Smarkm
24155682SmarkmIf a pointer to a credential cache handle is specified, and the handle
24255682Smarkmis NULL, a credential cache handle referring to all credentials
24355682Smarkmobtained in the course of verifying the user will be returned.  In
24455682Smarkmorder to avoid potential setuid race conditions and other problems
24555682Smarkmrelated to file system access, this handle will refer to a memory
24655682Smarkmcredential cache.  If the handle is non-NULL, then the credentials
24755682Smarkmwill be added to the existing ccache.  If the caller only wishes to
24855682Smarkmverify the password and will not be doing any other kerberos
24955682Smarkmfunctions, then a NULL pointer may be specified, and the credentials
25055682Smarkmwill be deleted before the function returns.
25155682Smarkm
25255682SmarkmIf ap_req_nofail is specified, then failures of the AP_REQ
25355682Smarkmverification are considered errors if and only if ap_req_nofail is
25455682Smarkmnon-zero.
25555682Smarkm
25655682SmarkmWhether or not AP_REQ validation is performed and what failures mean
25755682Smarkmdepends on these inputs:
25855682Smarkm
25955682Smarkm A) The appropriate keytab exists and contains the named key.
26055682Smarkm
26155682Smarkm B) An AP_REQ request to the kdc succeeds, and the resulting AP_REQ
26255682Smarkmcan be decrypted and verified.
26355682Smarkm
26455682Smarkm C) The administrator has specified in a configuration file that
26555682SmarkmAP_REQ validation must succeed.  This is basically a paranoid bit, and
26655682Smarkmcan be overridden by the application based on a command line flag or
26755682Smarkmother application-specific info.  This flag is especially useful if
26855682Smarkmthe admin is concerned that DNS might be spoofed while determining the
26955682Smarkmhost/FQDN name.  The configuration variable [libdefaults]
27055682Smarkm"verify_ap_req_nofail" is used if no value is passed in.  The library
27155682Smarkmdefault is not to set this option.
27255682Smarkm
27355682SmarkmInitial ticket verification will succeed if and only if:
27455682Smarkm
27555682Smarkm - A && B    or
27655682Smarkm - !A && !C
27755682Smarkm
27855682Smarkm================================================================
27955682Smarkm
28055682SmarkmFor illustrative purposes, here's the invocations I expect some
28155682Smarkmprograms will use.  Of course, error checking needs to be added.
28255682Smarkm
28355682Smarkmkinit:
28455682Smarkm
28555682Smarkm    /* Fill in client from the command line || existing ccache, and,
28655682Smarkm       start_time, and options.{tkt_life,renew_life,forwardable,proxiable}
28755682Smarkm       from the command line.  Some or all may remain unset. */
28855682Smarkm
28955682Smarkm    krb5_get_init_creds(context, &creds, client,
29055682Smarkm			krb5_initial_prompter_posix, NULL,
29155682Smarkm			start_time, NULL, &options);
29255682Smarkm    krb5_cc_store_cred(context, ccache, &creds);
29355682Smarkm    krb5_free_cred_contents(context, &creds);
29455682Smarkm
29555682Smarkmlogin:
29655682Smarkm
29755682Smarkm    krb5_get_init_creds(context, &creds, client,
29855682Smarkm			krb5_initial_prompter_posix, NULL,
29955682Smarkm			0, NULL, NULL);
30055682Smarkm    krb5_verify_init_creds(context, &creds, NULL, NULL, &vcc, NULL);
30155682Smarkm    /* setuid */
30255682Smarkm    krb5_cc_store_cred(context, ccache, &creds);
30355682Smarkm    krb5_cc_copy(context, vcc, ccache);
30455682Smarkm    krb5_free_cred_contents(context, &creds);
30555682Smarkm    krb5_cc_destroy(context, vcc);
30655682Smarkm
30755682Smarkmxdm:
30855682Smarkm
30955682Smarkm    krb5_get_initial_creds(context, &creds, client,
31055682Smarkm			   krb5_initial_prompter_xt, (void *) &xtstuff,
31155682Smarkm			   0, NULL, NULL);
31255682Smarkm    krb5_verify_init_creds(context, &creds, NULL, NULL, &vcc, NULL);
31355682Smarkm    /* setuid */
31455682Smarkm    krb5_cc_store_cred(context, ccache, &creds);
31555682Smarkm    krb5_free_cred_contents(context, &creds);
31655682Smarkm    krb5_cc_copy(context, vcc, ccache);
31755682Smarkm    krb5_cc_destroy(context, vcc);
31855682Smarkm
31955682Smarkmpasswd:
32055682Smarkm
32155682Smarkm    krb5_init_creds_opt_init(&options);
32255682Smarkm    krb5_init_creds_opt_set_tkt_life = 300;
32355682Smarkm    krb5_get_initial_creds(context, &creds, client,
32455682Smarkm			   krb5_initial_prompter_posix, NULL,
32555682Smarkm			   0, "kadmin/changepw", &options);
32655682Smarkm    /* change password */
32755682Smarkm    krb5_free_cred_contents(context, &creds);
32855682Smarkm
32955682Smarkmpop3d (simple password validator when no user interation possible):
33055682Smarkm
33155682Smarkm    krb5_get_initial_creds(context, &creds, client,
33255682Smarkm			   NULL, NULL, 0, NULL, NULL);
33355682Smarkm    krb5_verify_init_creds(context, &creds, NULL, NULL, &vcc, NULL);
33455682Smarkm    krb5_cc_destroy(context, vcc);
33555682Smarkm
33655682Smarkm================================================================
33755682Smarkm
33855682Smarkmpassword expiration has a subtlety.  When a password expires and is
33955682Smarkmchanged, there is a delay between when the master gets the new key
34055682Smarkm(immediately), and the slaves (propogation interval).  So, when
34155682Smarkmgetting an in_tkt, if the password is expired, the request should be
34255682Smarkmreissued to the master (this kind of sucks if you have SAM, oh well).
34355682SmarkmIf this says expired, too, then the password should be changed, and
34455682Smarkmthen the initial ticket request should be issued to the master again.
34555682SmarkmIf the master times out, then a message that the password has expired
34655682Smarkmand cannot be changed due to the master being unreachable should be
34755682Smarkmdisplayed.
34855682Smarkm
34955682Smarkm================================================================
35055682Smarkm
35155682Smarkmget_init_creds reads config stuff from:
35255682Smarkm
35355682Smarkm[libdefaults]
35455682Smarkm	varname1 = defvalue
35555682Smarkm	REALM = {
35655682Smarkm		varname1 = value
35755682Smarkm		varname2 = value
35855682Smarkm	}
35955682Smarkm
36055682Smarkmtypedef struct _krb5_get_init_creds_opt {
36155682Smarkm    krb5_flags flags;
36255682Smarkm    krb5_deltat tkt_life;	/* varname = "ticket_lifetime" */
36355682Smarkm    krb5_deltat renew_life;	/* varname = "renew_lifetime" */
36455682Smarkm    int forwardable;		/* varname = "forwardable" */
36555682Smarkm    int proxiable;		/* varname = "proxiable" */
36655682Smarkm    krb5_enctype *etype_list;	/* varname = "default_tkt_enctypes" */
36755682Smarkm    int etype_list_length;
36855682Smarkm    krb5_address **address_list; /* no varname */
36955682Smarkm    krb5_preauthtype *preauth_list; /* no varname */
37055682Smarkm    int preauth_list_length;
37155682Smarkm    krb5_data *salt;
37255682Smarkm} krb5_get_init_creds_opt;
37355682Smarkm
37455682Smarkm
375