2 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "krb5_locl.h"
36 RCSID("$Id: init_creds_pw.c 21931 2007-08-27 14:11:55Z lha $");
38 typedef struct krb5_get_init_creds_ctx {
41 krb5_addresses *addrs;
43 krb5_preauthtype *pre_auth_types;
44 const char *in_tkt_service;
53 krb5_s2k_proc key_proc;
55 krb5_get_init_creds_tristate req_pac;
57 krb5_pk_init_ctx pk_init_ctx;
59 } krb5_get_init_creds_ctx;
61 static krb5_error_code
62 default_s2k_func(krb5_context context, krb5_enctype type,
63 krb5_const_pointer keyseed,
64 krb5_salt salt, krb5_data *s2kparms,
71 password.data = rk_UNCONST(keyseed);
72 password.length = strlen(keyseed);
76 krb5_data_zero(&opaque);
78 *key = malloc(sizeof(**key));
81 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
91 free_init_creds_ctx(krb5_context context, krb5_get_init_creds_ctx *ctx)
95 if (ctx->pre_auth_types)
96 free (ctx->pre_auth_types);
97 free_AS_REQ(&ctx->as_req);
98 memset(&ctx->as_req, 0, sizeof(ctx->as_req));
102 get_config_time (krb5_context context,
109 ret = krb5_config_get_time (context, NULL,
116 ret = krb5_config_get_time (context, NULL,
125 static krb5_error_code
126 init_cred (krb5_context context,
128 krb5_principal client,
129 krb5_deltat start_time,
130 const char *in_tkt_service,
131 krb5_get_init_creds_opt *options)
134 krb5_const_realm client_realm;
138 krb5_timeofday (context, &now);
140 memset (cred, 0, sizeof(*cred));
143 krb5_copy_principal(context, client, &cred->client);
145 ret = krb5_get_default_principal (context,
151 client_realm = krb5_principal_get_realm (context, cred->client);
154 cred->times.starttime = now + start_time;
156 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
157 tmp = options->tkt_life;
160 cred->times.endtime = now + tmp;
162 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
163 options->renew_life > 0) {
164 cred->times.renew_till = now + options->renew_life;
167 if (in_tkt_service) {
168 krb5_realm server_realm;
170 ret = krb5_parse_name (context, in_tkt_service, &cred->server);
173 server_realm = strdup (client_realm);
174 free (*krb5_princ_realm(context, cred->server));
175 krb5_princ_set_realm (context, cred->server, &server_realm);
177 ret = krb5_make_principal(context, &cred->server,
178 client_realm, KRB5_TGS_NAME, client_realm,
186 krb5_free_cred_contents (context, cred);
191 * Print a message (str) to the user about the expiration in `lr'
195 report_expiration (krb5_context context,
196 krb5_prompter_fct prompter,
203 asprintf (&p, "%s%s", str, ctime(&now));
204 (*prompter) (context, data, NULL, p, 0, NULL);
209 * Parse the last_req data and show it to the user if it's interesting
213 print_expire (krb5_context context,
214 krb5_const_realm realm,
216 krb5_prompter_fct prompter,
220 LastReq *lr = &rep->enc_part.last_req;
223 krb5_boolean reported = FALSE;
225 krb5_timeofday (context, &sec);
227 t = sec + get_config_time (context,
232 for (i = 0; i < lr->len; ++i) {
233 if (lr->val[i].lr_value <= t) {
234 switch (abs(lr->val[i].lr_type)) {
236 report_expiration(context, prompter, data,
237 "Your password will expire at ",
238 lr->val[i].lr_value);
241 case LR_ACCT_EXPTIME :
242 report_expiration(context, prompter, data,
243 "Your account will expire at ",
244 lr->val[i].lr_value);
252 && rep->enc_part.key_expiration
253 && *rep->enc_part.key_expiration <= t) {
254 report_expiration(context, prompter, data,
255 "Your password/account will expire at ",
256 *rep->enc_part.key_expiration);
260 static krb5_addresses no_addrs = { 0, NULL };
262 static krb5_error_code
263 get_init_creds_common(krb5_context context,
264 krb5_principal client,
265 krb5_deltat start_time,
266 const char *in_tkt_service,
267 krb5_get_init_creds_opt *options,
268 krb5_get_init_creds_ctx *ctx)
270 krb5_get_init_creds_opt default_opt;
272 krb5_enctype *etypes;
273 krb5_preauthtype *pre_auth_types;
275 memset(ctx, 0, sizeof(*ctx));
277 if (options == NULL) {
278 krb5_get_init_creds_opt_init (&default_opt);
279 options = &default_opt;
281 _krb5_get_init_creds_opt_free_krb5_error(options);
284 if (options->opt_private) {
285 ctx->password = options->opt_private->password;
286 ctx->key_proc = options->opt_private->key_proc;
287 ctx->req_pac = options->opt_private->req_pac;
288 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
289 ctx->ic_flags = options->opt_private->flags;
291 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
293 if (ctx->key_proc == NULL)
294 ctx->key_proc = default_s2k_func;
296 if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
297 ctx->flags.canonicalize = 1;
299 ctx->pre_auth_types = NULL;
302 ctx->pre_auth_types = NULL;
303 ctx->in_tkt_service = in_tkt_service;
305 ret = init_cred (context, &ctx->cred, client, start_time,
306 in_tkt_service, options);
310 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
311 ctx->flags.forwardable = options->forwardable;
313 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
314 ctx->flags.proxiable = options->proxiable;
317 ctx->flags.postdated = 1;
318 if (ctx->cred.times.renew_till)
319 ctx->flags.renewable = 1;
320 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
321 ctx->addrs = options->address_list;
322 } else if (options->opt_private) {
323 switch (options->opt_private->addressless) {
324 case KRB5_INIT_CREDS_TRISTATE_UNSET:
325 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
326 ctx->addrs = &no_addrs;
331 case KRB5_INIT_CREDS_TRISTATE_FALSE:
334 case KRB5_INIT_CREDS_TRISTATE_TRUE:
335 ctx->addrs = &no_addrs;
339 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
340 etypes = malloc((options->etype_list_length + 1)
341 * sizeof(krb5_enctype));
342 if (etypes == NULL) {
343 krb5_set_error_string(context, "malloc: out of memory");
346 memcpy (etypes, options->etype_list,
347 options->etype_list_length * sizeof(krb5_enctype));
348 etypes[options->etype_list_length] = ETYPE_NULL;
349 ctx->etypes = etypes;
351 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
352 pre_auth_types = malloc((options->preauth_list_length + 1)
353 * sizeof(krb5_preauthtype));
354 if (pre_auth_types == NULL) {
355 krb5_set_error_string(context, "malloc: out of memory");
358 memcpy (pre_auth_types, options->preauth_list,
359 options->preauth_list_length * sizeof(krb5_preauthtype));
360 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
361 ctx->pre_auth_types = pre_auth_types;
363 if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)
365 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
366 ctx->flags.request_anonymous = options->anonymous;
370 static krb5_error_code
371 change_password (krb5_context context,
372 krb5_principal client,
373 const char *password,
376 krb5_prompter_fct prompter,
378 krb5_get_init_creds_opt *old_options)
380 krb5_prompt prompts[2];
383 char buf1[BUFSIZ], buf2[BUFSIZ];
384 krb5_data password_data[2];
386 krb5_data result_code_string;
387 krb5_data result_string;
389 krb5_get_init_creds_opt options;
391 memset (&cpw_cred, 0, sizeof(cpw_cred));
393 krb5_get_init_creds_opt_init (&options);
394 krb5_get_init_creds_opt_set_tkt_life (&options, 60);
395 krb5_get_init_creds_opt_set_forwardable (&options, FALSE);
396 krb5_get_init_creds_opt_set_proxiable (&options, FALSE);
397 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
398 krb5_get_init_creds_opt_set_preauth_list (&options,
399 old_options->preauth_list,
400 old_options->preauth_list_length);
402 krb5_data_zero (&result_code_string);
403 krb5_data_zero (&result_string);
405 ret = krb5_get_init_creds_password (context,
418 password_data[0].data = buf1;
419 password_data[0].length = sizeof(buf1);
421 prompts[0].hidden = 1;
422 prompts[0].prompt = "New password: ";
423 prompts[0].reply = &password_data[0];
424 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
426 password_data[1].data = buf2;
427 password_data[1].length = sizeof(buf2);
429 prompts[1].hidden = 1;
430 prompts[1].prompt = "Repeat new password: ";
431 prompts[1].reply = &password_data[1];
432 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
434 ret = (*prompter) (context, data, NULL, "Changing password",
437 memset (buf1, 0, sizeof(buf1));
438 memset (buf2, 0, sizeof(buf2));
442 if (strcmp (buf1, buf2) == 0)
444 memset (buf1, 0, sizeof(buf1));
445 memset (buf2, 0, sizeof(buf2));
448 ret = krb5_change_password (context,
456 asprintf (&p, "%s: %.*s\n",
457 result_code ? "Error" : "Success",
458 (int)result_string.length,
459 result_string.length > 0 ? (char*)result_string.data : "");
461 ret = (*prompter) (context, data, NULL, p, 0, NULL);
463 if (result_code == 0) {
464 strlcpy (newpw, buf1, newpw_sz);
467 krb5_set_error_string (context, "failed changing password");
472 memset (buf1, 0, sizeof(buf1));
473 memset (buf2, 0, sizeof(buf2));
474 krb5_data_free (&result_string);
475 krb5_data_free (&result_code_string);
476 krb5_free_cred_contents (context, &cpw_cred);
480 krb5_error_code KRB5_LIB_FUNCTION
481 krb5_keyblock_key_proc (krb5_context context,
484 krb5_const_pointer keyseed,
487 return krb5_copy_keyblock (context, keyseed, key);
490 krb5_error_code KRB5_LIB_FUNCTION
491 krb5_get_init_creds_keytab(krb5_context context,
493 krb5_principal client,
495 krb5_deltat start_time,
496 const char *in_tkt_service,
497 krb5_get_init_creds_opt *options)
499 krb5_get_init_creds_ctx ctx;
501 krb5_keytab_key_proc_args *a;
503 ret = get_init_creds_common(context, client, start_time,
504 in_tkt_service, options, &ctx);
508 a = malloc (sizeof(*a));
510 krb5_set_error_string(context, "malloc: out of memory");
514 a->principal = ctx.cred.client;
517 ret = krb5_get_in_cred (context,
518 KDCOptions2int(ctx.flags),
523 krb5_keytab_key_proc,
531 if (ret == 0 && creds)
534 krb5_free_cred_contents (context, &ctx.cred);
537 free_init_creds_ctx(context, &ctx);
545 static krb5_error_code
546 init_creds_init_as_req (krb5_context context,
548 const krb5_creds *creds,
549 const krb5_addresses *addrs,
550 const krb5_enctype *etypes,
555 memset(a, 0, sizeof(*a));
558 a->msg_type = krb_as_req;
559 a->req_body.kdc_options = opts;
560 a->req_body.cname = malloc(sizeof(*a->req_body.cname));
561 if (a->req_body.cname == NULL) {
563 krb5_set_error_string(context, "malloc: out of memory");
566 a->req_body.sname = malloc(sizeof(*a->req_body.sname));
567 if (a->req_body.sname == NULL) {
569 krb5_set_error_string(context, "malloc: out of memory");
573 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
576 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
580 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
584 if(creds->times.starttime) {
585 a->req_body.from = malloc(sizeof(*a->req_body.from));
586 if (a->req_body.from == NULL) {
588 krb5_set_error_string(context, "malloc: out of memory");
591 *a->req_body.from = creds->times.starttime;
593 if(creds->times.endtime){
594 ALLOC(a->req_body.till, 1);
595 *a->req_body.till = creds->times.endtime;
597 if(creds->times.renew_till){
598 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
599 if (a->req_body.rtime == NULL) {
601 krb5_set_error_string(context, "malloc: out of memory");
604 *a->req_body.rtime = creds->times.renew_till;
606 a->req_body.nonce = 0;
607 ret = krb5_init_etype (context,
608 &a->req_body.etype.len,
609 &a->req_body.etype.val,
615 * This means no addresses
618 if (addrs && addrs->len == 0) {
619 a->req_body.addresses = NULL;
621 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
622 if (a->req_body.addresses == NULL) {
624 krb5_set_error_string(context, "malloc: out of memory");
629 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
631 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
632 if(ret == 0 && a->req_body.addresses->len == 0) {
633 free(a->req_body.addresses);
634 a->req_body.addresses = NULL;
641 a->req_body.enc_authorization_data = NULL;
642 a->req_body.additional_tickets = NULL;
649 memset(a, 0, sizeof(*a));
653 struct pa_info_data {
656 krb5_data *s2kparams;
660 free_paid(krb5_context context, struct pa_info_data *ppaid)
662 krb5_free_salt(context, ppaid->salt);
663 if (ppaid->s2kparams)
664 krb5_free_data(context, ppaid->s2kparams);
668 static krb5_error_code
669 set_paid(struct pa_info_data *paid, krb5_context context,
671 krb5_salttype salttype, void *salt_string, size_t salt_len,
672 krb5_data *s2kparams)
675 paid->salt.salttype = salttype;
676 paid->salt.saltvalue.data = malloc(salt_len + 1);
677 if (paid->salt.saltvalue.data == NULL) {
678 krb5_clear_error_string(context);
681 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
682 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
683 paid->salt.saltvalue.length = salt_len;
687 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
689 krb5_clear_error_string(context);
690 krb5_free_salt(context, paid->salt);
694 paid->s2kparams = NULL;
699 static struct pa_info_data *
700 pa_etype_info2(krb5_context context,
701 const krb5_principal client,
703 struct pa_info_data *paid,
704 heim_octet_string *data)
711 memset(&e, 0, sizeof(e));
712 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
717 for (j = 0; j < asreq->req_body.etype.len; j++) {
718 for (i = 0; i < e.len; i++) {
719 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
721 if (e.val[i].salt == NULL)
722 ret = krb5_get_pw_salt(context, client, &salt);
724 salt.saltvalue.data = *e.val[i].salt;
725 salt.saltvalue.length = strlen(*e.val[i].salt);
729 ret = set_paid(paid, context, e.val[i].etype,
732 salt.saltvalue.length,
734 if (e.val[i].salt == NULL)
735 krb5_free_salt(context, salt);
737 free_ETYPE_INFO2(&e);
744 free_ETYPE_INFO2(&e);
748 static struct pa_info_data *
749 pa_etype_info(krb5_context context,
750 const krb5_principal client,
752 struct pa_info_data *paid,
753 heim_octet_string *data)
760 memset(&e, 0, sizeof(e));
761 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
766 for (j = 0; j < asreq->req_body.etype.len; j++) {
767 for (i = 0; i < e.len; i++) {
768 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
770 salt.salttype = KRB5_PW_SALT;
771 if (e.val[i].salt == NULL)
772 ret = krb5_get_pw_salt(context, client, &salt);
774 salt.saltvalue = *e.val[i].salt;
777 if (e.val[i].salttype)
778 salt.salttype = *e.val[i].salttype;
780 ret = set_paid(paid, context, e.val[i].etype,
783 salt.saltvalue.length,
785 if (e.val[i].salt == NULL)
786 krb5_free_salt(context, salt);
800 static struct pa_info_data *
801 pa_pw_or_afs3_salt(krb5_context context,
802 const krb5_principal client,
804 struct pa_info_data *paid,
805 heim_octet_string *data)
808 if (paid->etype == ENCTYPE_NULL)
810 ret = set_paid(paid, context,
823 krb5_preauthtype type;
824 struct pa_info_data *(*salt_info)(krb5_context,
825 const krb5_principal,
827 struct pa_info_data *,
828 heim_octet_string *);
831 static struct pa_info pa_prefs[] = {
832 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
833 { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
834 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
835 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
839 find_pa_data(const METHOD_DATA *md, int type)
844 for (i = 0; i < md->len; i++)
845 if (md->val[i].padata_type == type)
850 static struct pa_info_data *
851 process_pa_info(krb5_context context,
852 const krb5_principal client,
854 struct pa_info_data *paid,
857 struct pa_info_data *p = NULL;
860 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
861 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
864 paid->salt.salttype = pa_prefs[i].type;
865 p = (*pa_prefs[i].salt_info)(context, client, asreq,
866 paid, &pa->padata_value);
871 static krb5_error_code
872 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
873 krb5_enctype etype, krb5_keyblock *key)
879 EncryptedData encdata;
885 krb5_us_timeofday (context, &p.patimestamp, &usec);
889 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
893 krb5_abortx(context, "internal error in ASN.1 encoder");
895 ret = krb5_crypto_init(context, key, 0, &crypto);
900 ret = krb5_encrypt_EncryptedData(context,
902 KRB5_KU_PA_ENC_TIMESTAMP,
908 krb5_crypto_destroy(context, crypto);
912 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
913 free_EncryptedData(&encdata);
917 krb5_abortx(context, "internal error in ASN.1 encoder");
919 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
925 static krb5_error_code
926 add_enc_ts_padata(krb5_context context,
928 krb5_principal client,
929 krb5_s2k_proc key_proc,
930 krb5_const_pointer keyseed,
931 krb5_enctype *enctypes,
934 krb5_data *s2kparams)
942 /* default to standard salt */
943 ret = krb5_get_pw_salt (context, client, &salt2);
947 enctypes = context->etypes;
949 for (ep = enctypes; *ep != ETYPE_NULL; ep++)
953 for (i = 0; i < netypes; ++i) {
956 ret = (*key_proc)(context, enctypes[i], keyseed,
957 *salt, s2kparams, &key);
960 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
961 krb5_free_keyblock (context, key);
966 krb5_free_salt(context, salt2);
970 static krb5_error_code
971 pa_data_to_md_ts_enc(krb5_context context,
973 const krb5_principal client,
974 krb5_get_init_creds_ctx *ctx,
975 struct pa_info_data *ppaid,
978 if (ctx->key_proc == NULL || ctx->password == NULL)
982 add_enc_ts_padata(context, md, client,
983 ctx->key_proc, ctx->password,
985 &ppaid->salt, ppaid->s2kparams);
989 /* make a v5 salted pa-data */
990 add_enc_ts_padata(context, md, client,
991 ctx->key_proc, ctx->password,
992 a->req_body.etype.val, a->req_body.etype.len,
995 /* make a v4 salted pa-data */
996 salt.salttype = KRB5_PW_SALT;
997 krb5_data_zero(&salt.saltvalue);
998 add_enc_ts_padata(context, md, client,
999 ctx->key_proc, ctx->password,
1000 a->req_body.etype.val, a->req_body.etype.len,
1006 static krb5_error_code
1007 pa_data_to_key_plain(krb5_context context,
1008 const krb5_principal client,
1009 krb5_get_init_creds_ctx *ctx,
1011 krb5_data *s2kparams,
1013 krb5_keyblock **key)
1015 krb5_error_code ret;
1017 ret = (*ctx->key_proc)(context, etype, ctx->password,
1018 salt, s2kparams, key);
1023 static krb5_error_code
1024 pa_data_to_md_pkinit(krb5_context context,
1026 const krb5_principal client,
1027 krb5_get_init_creds_ctx *ctx,
1030 if (ctx->pk_init_ctx == NULL)
1033 return _krb5_pk_mk_padata(context,
1039 krb5_set_error_string(context, "no support for PKINIT compiled in");
1044 static krb5_error_code
1045 pa_data_add_pac_request(krb5_context context,
1046 krb5_get_init_creds_ctx *ctx,
1050 krb5_error_code ret;
1054 switch (ctx->req_pac) {
1055 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1056 return 0; /* don't bother */
1057 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1058 req.include_pac = 1;
1060 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1061 req.include_pac = 0;
1064 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1069 krb5_abortx(context, "internal error in ASN.1 encoder");
1071 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1079 * Assumes caller always will free `out_md', even on error.
1082 static krb5_error_code
1083 process_pa_data_to_md(krb5_context context,
1084 const krb5_creds *creds,
1086 krb5_get_init_creds_ctx *ctx,
1088 METHOD_DATA **out_md,
1089 krb5_prompter_fct prompter,
1090 void *prompter_data)
1092 krb5_error_code ret;
1095 if (*out_md == NULL) {
1096 krb5_set_error_string(context, "malloc: out of memory");
1100 (*out_md)->val = NULL;
1103 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1104 * need to expose our password protecting our PKCS12 key.
1107 if (ctx->pk_init_ctx) {
1109 ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
1113 } else if (in_md->len != 0) {
1114 struct pa_info_data paid, *ppaid;
1116 memset(&paid, 0, sizeof(paid));
1118 paid.etype = ENCTYPE_NULL;
1119 ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
1121 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1123 free_paid(context, ppaid);
1126 pa_data_add_pac_request(context, ctx, *out_md);
1128 if ((*out_md)->len == 0) {
1136 static krb5_error_code
1137 process_pa_data_to_key(krb5_context context,
1138 krb5_get_init_creds_ctx *ctx,
1142 const krb5_krbhst_info *hi,
1143 krb5_keyblock **key)
1145 struct pa_info_data paid, *ppaid = NULL;
1146 krb5_error_code ret;
1150 memset(&paid, 0, sizeof(paid));
1152 etype = rep->kdc_rep.enc_part.etype;
1154 if (rep->kdc_rep.padata) {
1156 ppaid = process_pa_info(context, creds->client, a, &paid,
1157 rep->kdc_rep.padata);
1159 if (ppaid == NULL) {
1160 ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1164 paid.s2kparams = NULL;
1168 if (rep->kdc_rep.padata) {
1170 pa = krb5_find_padata(rep->kdc_rep.padata->val,
1171 rep->kdc_rep.padata->len,
1172 KRB5_PADATA_PK_AS_REP,
1176 pa = krb5_find_padata(rep->kdc_rep.padata->val,
1177 rep->kdc_rep.padata->len,
1178 KRB5_PADATA_PK_AS_REP_19,
1182 if (pa && ctx->pk_init_ctx) {
1184 ret = _krb5_pk_rd_pa_reply(context,
1194 krb5_set_error_string(context, "no support for PKINIT compiled in");
1197 } else if (ctx->password)
1198 ret = pa_data_to_key_plain(context, creds->client, ctx,
1199 paid.salt, paid.s2kparams, etype, key);
1201 krb5_set_error_string(context, "No usable pa data type");
1205 free_paid(context, &paid);
1209 static krb5_error_code
1210 init_cred_loop(krb5_context context,
1211 krb5_get_init_creds_opt *init_cred_opts,
1212 const krb5_prompter_fct prompter,
1213 void *prompter_data,
1214 krb5_get_init_creds_ctx *ctx,
1216 krb5_kdc_rep *ret_as_reply)
1218 krb5_error_code ret;
1224 krb5_krbhst_info *hi = NULL;
1225 krb5_sendto_ctx stctx = NULL;
1228 memset(&md, 0, sizeof(md));
1229 memset(&rep, 0, sizeof(rep));
1231 _krb5_get_init_creds_opt_free_krb5_error(init_cred_opts);
1234 memset(ret_as_reply, 0, sizeof(*ret_as_reply));
1236 ret = init_creds_init_as_req(context, ctx->flags, creds,
1237 ctx->addrs, ctx->etypes, &ctx->as_req);
1241 ret = krb5_sendto_ctx_alloc(context, &stctx);
1244 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
1246 /* Set a new nonce. */
1247 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1248 ctx->nonce &= 0xffffffff;
1249 /* XXX these just needs to be the same when using Windows PK-INIT */
1250 ctx->pk_nonce = ctx->nonce;
1253 * Increase counter when we want other pre-auth types then
1254 * KRB5_PA_ENC_TIMESTAMP.
1256 #define MAX_PA_COUNTER 3
1258 ctx->pa_counter = 0;
1259 while (ctx->pa_counter < MAX_PA_COUNTER) {
1263 if (ctx->as_req.padata) {
1264 free_METHOD_DATA(ctx->as_req.padata);
1265 free(ctx->as_req.padata);
1266 ctx->as_req.padata = NULL;
1269 /* Set a new nonce. */
1270 ctx->as_req.req_body.nonce = ctx->nonce;
1272 /* fill_in_md_data */
1273 ret = process_pa_data_to_md(context, creds, &ctx->as_req, ctx,
1274 &md, &ctx->as_req.padata,
1275 prompter, prompter_data);
1279 krb5_data_free(&ctx->req_buffer);
1281 ASN1_MALLOC_ENCODE(AS_REQ,
1282 ctx->req_buffer.data, ctx->req_buffer.length,
1283 &ctx->as_req, &len, ret);
1286 if(len != ctx->req_buffer.length)
1287 krb5_abortx(context, "internal error in ASN.1 encoder");
1289 ret = krb5_sendto_context (context, stctx, &ctx->req_buffer,
1290 creds->client->realm, &resp);
1294 memset (&rep, 0, sizeof(rep));
1295 ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
1297 krb5_data_free(&resp);
1298 krb5_clear_error_string(context);
1301 /* let's try to parse it as a KRB-ERROR */
1304 ret = krb5_rd_error(context, &resp, &error);
1305 if(ret && resp.data && ((char*)resp.data)[0] == 4)
1306 ret = KRB5KRB_AP_ERR_V4_REPLY;
1307 krb5_data_free(&resp);
1311 ret = krb5_error_from_rd_error(context, &error, creds);
1314 * If no preauth was set and KDC requires it, give it one
1318 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1319 free_METHOD_DATA(&md);
1320 memset(&md, 0, sizeof(md));
1323 ret = decode_METHOD_DATA(error.e_data->data,
1324 error.e_data->length,
1328 krb5_set_error_string(context,
1329 "failed to decode METHOD DATA");
1331 /* XXX guess what the server want here add add md */
1333 krb5_free_error_contents(context, &error);
1337 _krb5_get_init_creds_opt_set_krb5_error(context,
1343 krb5_free_error_contents(context, &error);
1350 krb5_keyblock *key = NULL;
1353 if (ctx->flags.request_anonymous)
1354 flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
1355 if (ctx->flags.canonicalize) {
1356 flags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
1357 flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
1358 flags |= EXTRACT_TICKET_MATCH_REALM;
1361 ret = process_pa_data_to_key(context, ctx, creds,
1362 &ctx->as_req, &rep, hi, &key);
1366 ret = _krb5_extract_ticket(context,
1371 KRB5_KU_AS_REP_ENC_PART,
1377 krb5_free_keyblock(context, key);
1380 * Verify referral data
1382 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) &&
1383 (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) == 0)
1385 PA_ClientCanonicalized canon;
1391 pa = find_pa_data(rep.kdc_rep.padata, KRB5_PADATA_CLIENT_CANONICALIZED);
1394 krb5_set_error_string(context, "Client canonicalizion not signed");
1398 ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
1399 pa->padata_value.length,
1402 krb5_set_error_string(context, "Failed to decode "
1403 "PA_ClientCanonicalized");
1407 ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
1408 &canon.names, &len, ret);
1411 if (data.length != len)
1412 krb5_abortx(context, "internal asn.1 error");
1414 ret = krb5_crypto_init(context, &creds->session, 0, &crypto);
1417 free_PA_ClientCanonicalized(&canon);
1421 ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
1422 data.data, data.length,
1423 &canon.canon_checksum);
1424 krb5_crypto_destroy(context, crypto);
1426 free_PA_ClientCanonicalized(&canon);
1428 krb5_set_error_string(context, "Failed to verify "
1429 "client canonicalized data");
1435 krb5_sendto_ctx_free(context, stctx);
1436 krb5_data_free(&ctx->req_buffer);
1437 free_METHOD_DATA(&md);
1438 memset(&md, 0, sizeof(md));
1440 if (ret == 0 && ret_as_reply)
1441 *ret_as_reply = rep;
1443 krb5_free_kdc_rep (context, &rep);
1447 krb5_error_code KRB5_LIB_FUNCTION
1448 krb5_get_init_creds(krb5_context context,
1450 krb5_principal client,
1451 krb5_prompter_fct prompter,
1453 krb5_deltat start_time,
1454 const char *in_tkt_service,
1455 krb5_get_init_creds_opt *options)
1457 krb5_get_init_creds_ctx ctx;
1458 krb5_kdc_rep kdc_reply;
1459 krb5_error_code ret;
1463 memset(&kdc_reply, 0, sizeof(kdc_reply));
1465 ret = get_init_creds_common(context, client, start_time,
1466 in_tkt_service, options, &ctx);
1472 memset(&kdc_reply, 0, sizeof(kdc_reply));
1474 ret = init_cred_loop(context,
1486 case KRB5KDC_ERR_KEY_EXPIRED :
1487 /* try to avoid recursion */
1489 /* don't try to change password where then where none */
1490 if (prompter == NULL || ctx.password == NULL)
1493 krb5_clear_error_string (context);
1495 if (ctx.in_tkt_service != NULL
1496 && strcmp (ctx.in_tkt_service, "kadmin/changepw") == 0)
1499 ret = change_password (context,
1517 print_expire (context,
1518 krb5_principal_get_realm (context, ctx.cred.client),
1524 memset (buf, 0, sizeof(buf));
1525 free_init_creds_ctx(context, &ctx);
1526 krb5_free_kdc_rep (context, &kdc_reply);
1530 krb5_free_cred_contents (context, &ctx.cred);
1535 krb5_error_code KRB5_LIB_FUNCTION
1536 krb5_get_init_creds_password(krb5_context context,
1538 krb5_principal client,
1539 const char *password,
1540 krb5_prompter_fct prompter,
1542 krb5_deltat start_time,
1543 const char *in_tkt_service,
1544 krb5_get_init_creds_opt *in_options)
1546 krb5_get_init_creds_opt *options;
1548 krb5_error_code ret;
1550 if (in_options == NULL) {
1551 const char *realm = krb5_principal_get_realm(context, client);
1552 ret = krb5_get_init_creds_opt_alloc(context, &options);
1554 krb5_get_init_creds_opt_set_default_flags(context,
1559 ret = _krb5_get_init_creds_opt_copy(context, in_options, &options);
1563 if (password == NULL &&
1564 options->opt_private->password == NULL &&
1565 options->opt_private->pk_init_ctx == NULL)
1568 krb5_data password_data;
1571 krb5_unparse_name (context, client, &p);
1572 asprintf (&q, "%s's Password: ", p);
1575 password_data.data = buf;
1576 password_data.length = sizeof(buf);
1578 prompt.reply = &password_data;
1579 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1581 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
1584 memset (buf, 0, sizeof(buf));
1585 krb5_get_init_creds_opt_free(context, options);
1586 ret = KRB5_LIBOS_PWDINTR;
1587 krb5_clear_error_string (context);
1590 password = password_data.data;
1593 if (options->opt_private->password == NULL) {
1594 ret = krb5_get_init_creds_opt_set_pa_password(context, options,
1597 krb5_get_init_creds_opt_free(context, options);
1598 memset(buf, 0, sizeof(buf));
1603 ret = krb5_get_init_creds(context, creds, client, prompter,
1604 data, start_time, in_tkt_service, options);
1605 krb5_get_init_creds_opt_free(context, options);
1606 memset(buf, 0, sizeof(buf));
1610 static krb5_error_code
1611 init_creds_keyblock_key_proc (krb5_context context,
1614 krb5_const_pointer keyseed,
1615 krb5_keyblock **key)
1617 return krb5_copy_keyblock (context, keyseed, key);
1620 krb5_error_code KRB5_LIB_FUNCTION
1621 krb5_get_init_creds_keyblock(krb5_context context,
1623 krb5_principal client,
1624 krb5_keyblock *keyblock,
1625 krb5_deltat start_time,
1626 const char *in_tkt_service,
1627 krb5_get_init_creds_opt *options)
1629 struct krb5_get_init_creds_ctx ctx;
1630 krb5_error_code ret;
1632 ret = get_init_creds_common(context, client, start_time,
1633 in_tkt_service, options, &ctx);
1637 ret = krb5_get_in_cred (context,
1638 KDCOptions2int(ctx.flags),
1643 init_creds_keyblock_key_proc,
1650 if (ret == 0 && creds)
1653 krb5_free_cred_contents (context, &ctx.cred);
1656 free_init_creds_ctx(context, &ctx);