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