1Initial credentials 2=================== 3 4Software that performs tasks such as logging users into a computer 5when they type their Kerberos password needs to get initial 6credentials (usually ticket granting tickets) from Kerberos. Such 7software shares some behavior with the :ref:`kinit(1)` program. 8 9Whenever a program grants access to a resource (such as a local login 10session on a desktop computer) based on a user successfully getting 11initial Kerberos credentials, it must verify those credentials against 12a secure shared secret (e.g., a host keytab) to ensure that the user 13credentials actually originate from a legitimate KDC. Failure to 14perform this verification is a critical vulnerability, because a 15malicious user can execute the "Zanarotti attack": the user constructs 16a fake response that appears to come from the legitimate KDC, but 17whose contents come from an attacker-controlled KDC. 18 19Some applications read a Kerberos password over the network (ideally 20over a secure channel), which they then verify against the KDC. While 21this technique may be the only practical way to integrate Kerberos 22into some existing legacy systems, its use is contrary to the original 23design goals of Kerberos. 24 25The function :c:func:`krb5_get_init_creds_password` will get initial 26credentials for a client using a password. An application that needs 27to verify the credentials can call :c:func:`krb5_verify_init_creds`. 28Here is an example of code to obtain and verify TGT credentials, given 29strings *princname* and *password* for the client principal name and 30password:: 31 32 krb5_error_code ret; 33 krb5_creds creds; 34 krb5_principal client_princ = NULL; 35 36 memset(&creds, 0, sizeof(creds)); 37 ret = krb5_parse_name(context, princname, &client_princ); 38 if (ret) 39 goto cleanup; 40 ret = krb5_get_init_creds_password(context, &creds, client_princ, 41 password, NULL, NULL, 0, NULL, NULL); 42 if (ret) 43 goto cleanup; 44 ret = krb5_verify_init_creds(context, &creds, NULL, NULL, NULL, NULL); 45 46 cleanup: 47 krb5_free_principal(context, client_princ); 48 krb5_free_cred_contents(context, &creds); 49 return ret; 50 51Options for get_init_creds 52-------------------------- 53 54The function :c:func:`krb5_get_init_creds_password` takes an options 55parameter (which can be a null pointer). Use the function 56:c:func:`krb5_get_init_creds_opt_alloc` to allocate an options 57structure, and :c:func:`krb5_get_init_creds_opt_free` to free it. For 58example:: 59 60 krb5_error_code ret; 61 krb5_get_init_creds_opt *opt = NULL; 62 krb5_creds creds; 63 64 memset(&creds, 0, sizeof(creds)); 65 ret = krb5_get_init_creds_opt_alloc(context, &opt); 66 if (ret) 67 goto cleanup; 68 krb5_get_init_creds_opt_set_tkt_life(opt, 24 * 60 * 60); 69 ret = krb5_get_init_creds_password(context, &creds, client_princ, 70 password, NULL, NULL, 0, NULL, opt); 71 if (ret) 72 goto cleanup; 73 74 cleanup: 75 krb5_get_init_creds_opt_free(context, opt); 76 krb5_free_cred_contents(context, &creds); 77 return ret; 78 79Getting anonymous credentials 80----------------------------- 81 82As of release 1.8, it is possible to obtain fully anonymous or 83partially anonymous (realm-exposed) credentials, if the KDC supports 84it. The MIT KDC supports issuing fully anonymous credentials as of 85release 1.8 if configured appropriately (see :ref:`anonymous_pkinit`), 86but does not support issuing realm-exposed anonymous credentials at 87this time. 88 89To obtain fully anonymous credentials, call 90:c:func:`krb5_get_init_creds_opt_set_anonymous` on the options 91structure to set the anonymous flag, and specify a client principal 92with the KDC's realm and a single empty data component (the principal 93obtained by parsing ``@``\ *realmname*). Authentication will take 94place using anonymous PKINIT; if successful, the client principal of 95the resulting tickets will be 96``WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS``. Here is an example:: 97 98 krb5_get_init_creds_opt_set_anonymous(opt, 1); 99 ret = krb5_build_principal(context, &client_princ, strlen(myrealm), 100 myrealm, "", (char *)NULL); 101 if (ret) 102 goto cleanup; 103 ret = krb5_get_init_creds_password(context, &creds, client_princ, 104 password, NULL, NULL, 0, NULL, opt); 105 if (ret) 106 goto cleanup; 107 108To obtain realm-exposed anonymous credentials, set the anonymous flag 109on the options structure as above, but specify a normal client 110principal in order to prove membership in the realm. Authentication 111will take place as it normally does; if successful, the client 112principal of the resulting tickets will be ``WELLKNOWN/ANONYMOUS@``\ 113*realmname*. 114 115User interaction 116---------------- 117 118Authenticating a user usually requires the entry of secret 119information, such as a password. A password can be supplied directly 120to :c:func:`krb5_get_init_creds_password` via the *password* 121parameter, or the application can supply prompter and/or responder 122callbacks instead. If callbacks are used, the user can also be 123queried for other secret information such as a PIN, informed of 124impending password expiration, or prompted to change a password which 125has expired. 126 127Prompter callback 128~~~~~~~~~~~~~~~~~ 129 130A prompter callback can be specified via the *prompter* and *data* 131parameters to :c:func:`krb5_get_init_creds_password`. The prompter 132will be invoked each time the krb5 library has a question to ask or 133information to present. When the prompter callback is invoked, the 134*banner* argument (if not null) is intended to be displayed to the 135user, and the questions to be answered are specified in the *prompts* 136array. Each prompt contains a text question in the *prompt* field, a 137*hidden* bit to indicate whether the answer should be hidden from 138display, and a storage area for the answer in the *reply* field. The 139callback should fill in each question's ``reply->data`` with the 140answer, up to a maximum number of ``reply->length`` bytes, and then 141reset ``reply->length`` to the length of the answer. 142 143A prompter callback can call :c:func:`krb5_get_prompt_types` to get an 144array of type constants corresponding to the prompts, to get 145programmatic information about the semantic meaning of the questions. 146:c:func:`krb5_get_prompt_types` may return a null pointer if no prompt 147type information is available. 148 149Text-based applications can use a built-in text prompter 150implementation by supplying :c:func:`krb5_prompter_posix` as the 151*prompter* parameter and a null pointer as the *data* parameter. For 152example:: 153 154 ret = krb5_get_init_creds_password(context, &creds, client_princ, 155 NULL, krb5_prompter_posix, NULL, 0, 156 NULL, NULL); 157 158Responder callback 159~~~~~~~~~~~~~~~~~~ 160 161A responder callback can be specified through the init_creds options 162using the :c:func:`krb5_get_init_creds_opt_set_responder` function. 163Responder callbacks can present a more sophisticated user interface 164for authentication secrets. The responder callback is usually invoked 165only once per authentication, with a list of questions produced by all 166of the allowed preauthentication mechanisms. 167 168When the responder callback is invoked, the *rctx* argument can be 169accessed to obtain the list of questions and to answer them. The 170:c:func:`krb5_responder_list_questions` function retrieves an array of 171question types. For each question type, the 172:c:func:`krb5_responder_get_challenge` function retrieves additional 173information about the question, if applicable, and the 174:c:func:`krb5_responder_set_answer` function sets the answer. 175 176Responder question types, challenges, and answers are UTF-8 strings. 177The question type is a well-known string; the meaning of the challenge 178and answer depend on the question type. If an application does not 179understand a question type, it cannot interpret the challenge or 180provide an answer. Failing to answer a question typically results in 181the prompter callback being used as a fallback. 182 183Password question 184################# 185 186The :c:macro:`KRB5_RESPONDER_QUESTION_PASSWORD` (or ``"password"``) 187question type requests the user's password. This question does not 188have a challenge, and the response is simply the password string. 189 190One-time password question 191########################## 192 193The :c:macro:`KRB5_RESPONDER_QUESTION_OTP` (or ``"otp"``) question 194type requests a choice among one-time password tokens and the PIN and 195value for the chosen token. The challenge and answer are JSON-encoded 196strings, but an application can use convenience functions to avoid 197doing any JSON processing itself. 198 199The :c:func:`krb5_responder_otp_get_challenge` function decodes the 200challenge into a krb5_responder_otp_challenge structure. The 201:c:func:`krb5_responder_otp_set_answer` function selects one of the 202token information elements from the challenge and supplies the value 203and pin for that token. 204 205PKINIT password or PIN question 206############################### 207 208The :c:macro:`KRB5_RESPONDER_QUESTION_PKINIT` (or ``"pkinit"``) question 209type requests PINs for hardware devices and/or passwords for encrypted 210credentials which are stored on disk, potentially also supplying 211information about the state of the hardware devices. The challenge and 212answer are JSON-encoded strings, but an application can use convenience 213functions to avoid doing any JSON processing itself. 214 215The :c:func:`krb5_responder_pkinit_get_challenge` function decodes the 216challenges into a krb5_responder_pkinit_challenge structure. The 217:c:func:`krb5_responder_pkinit_set_answer` function can be used to 218supply the PIN or password for a particular client credential, and can 219be called multiple times. 220 221Example 222####### 223 224Here is an example of using a responder callback:: 225 226 static krb5_error_code 227 my_responder(krb5_context context, void *data, 228 krb5_responder_context rctx) 229 { 230 krb5_error_code ret; 231 krb5_responder_otp_challenge *chl; 232 233 if (krb5_responder_get_challenge(context, rctx, 234 KRB5_RESPONDER_QUESTION_PASSWORD)) { 235 ret = krb5_responder_set_answer(context, rctx, 236 KRB5_RESPONDER_QUESTION_PASSWORD, 237 "open sesame"); 238 if (ret) 239 return ret; 240 } 241 ret = krb5_responder_otp_get_challenge(context, rctx, &chl); 242 if (ret == 0 && chl != NULL) { 243 ret = krb5_responder_otp_set_answer(context, rctx, 0, "1234", 244 NULL); 245 krb5_responder_otp_challenge_free(context, rctx, chl); 246 if (ret) 247 return ret; 248 } 249 return 0; 250 } 251 252 static krb5_error_code 253 get_creds(krb5_context context, krb5_principal client_princ) 254 { 255 krb5_error_code ret; 256 krb5_get_init_creds_opt *opt = NULL; 257 krb5_creds creds; 258 259 memset(&creds, 0, sizeof(creds)); 260 ret = krb5_get_init_creds_opt_alloc(context, &opt); 261 if (ret) 262 goto cleanup; 263 ret = krb5_get_init_creds_opt_set_responder(context, opt, my_responder, 264 NULL); 265 if (ret) 266 goto cleanup; 267 ret = krb5_get_init_creds_password(context, &creds, client_princ, 268 NULL, NULL, NULL, 0, NULL, opt); 269 270 cleanup: 271 krb5_get_init_creds_opt_free(context, opt); 272 krb5_free_cred_contents(context, &creds); 273 return ret; 274 } 275 276Verifying initial credentials 277----------------------------- 278 279Use the function :c:func:`krb5_verify_init_creds` to verify initial 280credentials. It takes an options structure (which can be a null 281pointer). Use :c:func:`krb5_verify_init_creds_opt_init` to initialize 282the caller-allocated options structure, and 283:c:func:`krb5_verify_init_creds_opt_set_ap_req_nofail` to set the 284"nofail" option. For example:: 285 286 krb5_verify_init_creds_opt vopt; 287 288 krb5_verify_init_creds_opt_init(&vopt); 289 krb5_verify_init_creds_opt_set_ap_req_nofail(&vopt, 1); 290 ret = krb5_verify_init_creds(context, &creds, NULL, NULL, NULL, &vopt); 291 292The confusingly named "nofail" option, when set, means that the 293verification must actually succeed in order for 294:c:func:`krb5_verify_init_creds` to indicate success. The default 295state of this option (cleared) means that if there is no key material 296available to verify the user credentials, the verification will 297succeed anyway. (The default can be changed by a configuration file 298setting.) 299 300This accommodates a use case where a large number of unkeyed shared 301desktop workstations need to allow users to log in using Kerberos. 302The security risks from this practice are mitigated by the absence of 303valuable state on the shared workstations---any valuable resources 304that the users would access reside on networked servers. 305