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