2 * Copyright (c) 1997 - 2005 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,v 1.94 2006/04/24 08:49:08 lha Exp $");
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_req_pac req_pac;
57 krb5_pk_init_ctx pk_init_ctx;
58 } krb5_get_init_creds_ctx;
60 static krb5_error_code
61 default_s2k_func(krb5_context context, krb5_enctype type,
62 krb5_const_pointer keyseed,
63 krb5_salt salt, krb5_data *s2kparms,
70 password.data = rk_UNCONST(keyseed);
71 password.length = strlen(keyseed);
75 krb5_data_zero(&opaque);
77 *key = malloc(sizeof(**key));
80 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
90 free_init_creds_ctx(krb5_context context, krb5_get_init_creds_ctx *ctx)
94 if (ctx->pre_auth_types)
95 free (ctx->pre_auth_types);
96 free_AS_REQ(&ctx->as_req);
97 memset(&ctx->as_req, 0, sizeof(ctx->as_req));
101 get_config_time (krb5_context context,
108 ret = krb5_config_get_time (context, NULL,
115 ret = krb5_config_get_time (context, NULL,
124 static krb5_error_code
125 init_cred (krb5_context context,
127 krb5_principal client,
128 krb5_deltat start_time,
129 const char *in_tkt_service,
130 krb5_get_init_creds_opt *options)
133 krb5_const_realm client_realm;
137 krb5_timeofday (context, &now);
139 memset (cred, 0, sizeof(*cred));
142 krb5_copy_principal(context, client, &cred->client);
144 ret = krb5_get_default_principal (context,
150 client_realm = krb5_principal_get_realm (context, cred->client);
153 cred->times.starttime = now + start_time;
155 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
156 tmp = options->tkt_life;
159 cred->times.endtime = now + tmp;
161 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
162 options->renew_life > 0) {
163 cred->times.renew_till = now + options->renew_life;
166 if (in_tkt_service) {
167 krb5_realm server_realm;
169 ret = krb5_parse_name (context, in_tkt_service, &cred->server);
172 server_realm = strdup (client_realm);
173 free (*krb5_princ_realm(context, cred->server));
174 krb5_princ_set_realm (context, cred->server, &server_realm);
176 ret = krb5_make_principal(context, &cred->server,
177 client_realm, KRB5_TGS_NAME, client_realm,
185 krb5_free_cred_contents (context, cred);
190 * Print a message (str) to the user about the expiration in `lr'
194 report_expiration (krb5_context context,
195 krb5_prompter_fct prompter,
202 asprintf (&p, "%s%s", str, ctime(&now));
203 (*prompter) (context, data, NULL, p, 0, NULL);
208 * Parse the last_req data and show it to the user if it's interesting
212 print_expire (krb5_context context,
213 krb5_const_realm realm,
215 krb5_prompter_fct prompter,
219 LastReq *lr = &rep->enc_part.last_req;
222 krb5_boolean reported = FALSE;
224 krb5_timeofday (context, &sec);
226 t = sec + get_config_time (context,
231 for (i = 0; i < lr->len; ++i) {
232 if (lr->val[i].lr_value <= t) {
233 switch (abs(lr->val[i].lr_type)) {
235 report_expiration(context, prompter, data,
236 "Your password will expire at ",
237 lr->val[i].lr_value);
240 case LR_ACCT_EXPTIME :
241 report_expiration(context, prompter, data,
242 "Your account will expire at ",
243 lr->val[i].lr_value);
251 && rep->enc_part.key_expiration
252 && *rep->enc_part.key_expiration <= t) {
253 report_expiration(context, prompter, data,
254 "Your password/account will expire at ",
255 *rep->enc_part.key_expiration);
259 static krb5_error_code
260 get_init_creds_common(krb5_context context,
262 krb5_principal client,
263 krb5_deltat start_time,
264 const char *in_tkt_service,
265 krb5_get_init_creds_opt *options,
266 krb5_get_init_creds_ctx *ctx)
268 krb5_get_init_creds_opt default_opt;
270 krb5_enctype *etypes;
271 krb5_preauthtype *pre_auth_types;
273 memset(ctx, 0, sizeof(*ctx));
275 if (options == NULL) {
276 krb5_get_init_creds_opt_init (&default_opt);
277 options = &default_opt;
280 if (options->opt_private) {
281 ctx->password = options->opt_private->password;
282 ctx->key_proc = options->opt_private->key_proc;
283 ctx->req_pac = options->opt_private->req_pac;
284 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
286 ctx->req_pac = KRB5_PA_PAC_DONT_CARE;
288 if (ctx->key_proc == NULL)
289 ctx->key_proc = default_s2k_func;
291 ctx->pre_auth_types = NULL;
295 ctx->pre_auth_types = NULL;
296 ctx->in_tkt_service = in_tkt_service;
298 ret = init_cred (context, &ctx->cred, client, start_time,
299 in_tkt_service, options);
305 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
306 ctx->flags.b.forwardable = options->forwardable;
308 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
309 ctx->flags.b.proxiable = options->proxiable;
312 ctx->flags.b.postdated = 1;
313 if (ctx->cred.times.renew_till)
314 ctx->flags.b.renewable = 1;
315 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)
316 ctx->addrs = options->address_list;
317 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
318 etypes = malloc((options->etype_list_length + 1)
319 * sizeof(krb5_enctype));
320 if (etypes == NULL) {
321 krb5_set_error_string(context, "malloc: out of memory");
324 memcpy (etypes, options->etype_list,
325 options->etype_list_length * sizeof(krb5_enctype));
326 etypes[options->etype_list_length] = ETYPE_NULL;
327 ctx->etypes = etypes;
329 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
330 pre_auth_types = malloc((options->preauth_list_length + 1)
331 * sizeof(krb5_preauthtype));
332 if (pre_auth_types == NULL) {
333 krb5_set_error_string(context, "malloc: out of memory");
336 memcpy (pre_auth_types, options->preauth_list,
337 options->preauth_list_length * sizeof(krb5_preauthtype));
338 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
339 ctx->pre_auth_types = pre_auth_types;
341 if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)
343 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
344 ctx->flags.b.request_anonymous = options->anonymous;
348 static krb5_error_code
349 change_password (krb5_context context,
350 krb5_principal client,
351 const char *password,
354 krb5_prompter_fct prompter,
356 krb5_get_init_creds_opt *old_options)
358 krb5_prompt prompts[2];
361 char buf1[BUFSIZ], buf2[BUFSIZ];
362 krb5_data password_data[2];
364 krb5_data result_code_string;
365 krb5_data result_string;
367 krb5_get_init_creds_opt options;
369 memset (&cpw_cred, 0, sizeof(cpw_cred));
371 krb5_get_init_creds_opt_init (&options);
372 krb5_get_init_creds_opt_set_tkt_life (&options, 60);
373 krb5_get_init_creds_opt_set_forwardable (&options, FALSE);
374 krb5_get_init_creds_opt_set_proxiable (&options, FALSE);
375 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
376 krb5_get_init_creds_opt_set_preauth_list (&options,
377 old_options->preauth_list,
378 old_options->preauth_list_length);
380 krb5_data_zero (&result_code_string);
381 krb5_data_zero (&result_string);
383 ret = krb5_get_init_creds_password (context,
396 password_data[0].data = buf1;
397 password_data[0].length = sizeof(buf1);
399 prompts[0].hidden = 1;
400 prompts[0].prompt = "New password: ";
401 prompts[0].reply = &password_data[0];
402 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
404 password_data[1].data = buf2;
405 password_data[1].length = sizeof(buf2);
407 prompts[1].hidden = 1;
408 prompts[1].prompt = "Repeat new password: ";
409 prompts[1].reply = &password_data[1];
410 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
412 ret = (*prompter) (context, data, NULL, "Changing password",
415 memset (buf1, 0, sizeof(buf1));
416 memset (buf2, 0, sizeof(buf2));
420 if (strcmp (buf1, buf2) == 0)
422 memset (buf1, 0, sizeof(buf1));
423 memset (buf2, 0, sizeof(buf2));
426 ret = krb5_change_password (context,
434 asprintf (&p, "%s: %.*s\n",
435 result_code ? "Error" : "Success",
436 (int)result_string.length,
437 result_string.length > 0 ? (char*)result_string.data : "");
439 ret = (*prompter) (context, data, NULL, p, 0, NULL);
441 if (result_code == 0) {
442 strlcpy (newpw, buf1, newpw_sz);
445 krb5_set_error_string (context, "failed changing password");
450 memset (buf1, 0, sizeof(buf1));
451 memset (buf2, 0, sizeof(buf2));
452 krb5_data_free (&result_string);
453 krb5_data_free (&result_code_string);
454 krb5_free_cred_contents (context, &cpw_cred);
458 krb5_error_code KRB5_LIB_FUNCTION
459 krb5_keyblock_key_proc (krb5_context context,
462 krb5_const_pointer keyseed,
465 return krb5_copy_keyblock (context, keyseed, key);
468 krb5_error_code KRB5_LIB_FUNCTION
469 krb5_get_init_creds_keytab(krb5_context context,
471 krb5_principal client,
473 krb5_deltat start_time,
474 const char *in_tkt_service,
475 krb5_get_init_creds_opt *options)
477 krb5_get_init_creds_ctx ctx;
479 krb5_keytab_key_proc_args *a;
481 ret = get_init_creds_common(context, creds, client, start_time,
482 in_tkt_service, options, &ctx);
486 a = malloc (sizeof(*a));
488 krb5_set_error_string(context, "malloc: out of memory");
492 a->principal = ctx.cred.client;
495 ret = krb5_get_in_cred (context,
501 krb5_keytab_key_proc,
509 if (ret == 0 && creds)
512 krb5_free_cred_contents (context, &ctx.cred);
515 free_init_creds_ctx(context, &ctx);
523 static krb5_error_code
524 init_creds_init_as_req (krb5_context context,
526 const krb5_creds *creds,
527 const krb5_addresses *addrs,
528 const krb5_enctype *etypes,
533 memset(a, 0, sizeof(*a));
536 a->msg_type = krb_as_req;
537 a->req_body.kdc_options = opts.b;
538 a->req_body.cname = malloc(sizeof(*a->req_body.cname));
539 if (a->req_body.cname == NULL) {
541 krb5_set_error_string(context, "malloc: out of memory");
544 a->req_body.sname = malloc(sizeof(*a->req_body.sname));
545 if (a->req_body.sname == NULL) {
547 krb5_set_error_string(context, "malloc: out of memory");
551 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
554 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
558 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
562 if(creds->times.starttime) {
563 a->req_body.from = malloc(sizeof(*a->req_body.from));
564 if (a->req_body.from == NULL) {
566 krb5_set_error_string(context, "malloc: out of memory");
569 *a->req_body.from = creds->times.starttime;
571 if(creds->times.endtime){
572 ALLOC(a->req_body.till, 1);
573 *a->req_body.till = creds->times.endtime;
575 if(creds->times.renew_till){
576 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
577 if (a->req_body.rtime == NULL) {
579 krb5_set_error_string(context, "malloc: out of memory");
582 *a->req_body.rtime = creds->times.renew_till;
584 a->req_body.nonce = 0;
585 ret = krb5_init_etype (context,
586 &a->req_body.etype.len,
587 &a->req_body.etype.val,
593 * This means no addresses
596 if (addrs && addrs->len == 0) {
597 a->req_body.addresses = NULL;
599 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
600 if (a->req_body.addresses == NULL) {
602 krb5_set_error_string(context, "malloc: out of memory");
607 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
609 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
610 if(ret == 0 && a->req_body.addresses->len == 0) {
611 free(a->req_body.addresses);
612 a->req_body.addresses = NULL;
619 a->req_body.enc_authorization_data = NULL;
620 a->req_body.additional_tickets = NULL;
627 memset(a, 0, sizeof(*a));
631 struct pa_info_data {
634 krb5_data *s2kparams;
638 free_paid(krb5_context context, struct pa_info_data *ppaid)
640 krb5_free_salt(context, ppaid->salt);
641 if (ppaid->s2kparams)
642 krb5_data_free(ppaid->s2kparams);
646 static krb5_error_code
647 set_paid(struct pa_info_data *paid, krb5_context context,
649 krb5_salttype salttype, void *salt_string, size_t salt_len,
650 krb5_data *s2kparams)
653 paid->salt.salttype = salttype;
654 paid->salt.saltvalue.data = malloc(salt_len + 1);
655 if (paid->salt.saltvalue.data == NULL) {
656 krb5_clear_error_string(context);
659 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
660 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
661 paid->salt.saltvalue.length = salt_len;
665 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
667 krb5_clear_error_string(context);
668 krb5_free_salt(context, paid->salt);
672 paid->s2kparams = NULL;
677 static struct pa_info_data *
678 pa_etype_info2(krb5_context context,
679 const krb5_principal client,
681 struct pa_info_data *paid,
682 heim_octet_string *data)
689 memset(&e, 0, sizeof(e));
690 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
695 for (j = 0; j < asreq->req_body.etype.len; j++) {
696 for (i = 0; i < e.len; i++) {
697 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
699 if (e.val[i].salt == NULL)
700 ret = krb5_get_pw_salt(context, client, &salt);
702 salt.saltvalue.data = *e.val[i].salt;
703 salt.saltvalue.length = strlen(*e.val[i].salt);
707 ret = set_paid(paid, context, e.val[i].etype,
710 salt.saltvalue.length,
712 if (e.val[i].salt == NULL)
713 krb5_free_salt(context, salt);
715 free_ETYPE_INFO2(&e);
722 free_ETYPE_INFO2(&e);
726 static struct pa_info_data *
727 pa_etype_info(krb5_context context,
728 const krb5_principal client,
730 struct pa_info_data *paid,
731 heim_octet_string *data)
738 memset(&e, 0, sizeof(e));
739 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
744 for (j = 0; j < asreq->req_body.etype.len; j++) {
745 for (i = 0; i < e.len; i++) {
746 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
748 salt.salttype = KRB5_PW_SALT;
749 if (e.val[i].salt == NULL)
750 ret = krb5_get_pw_salt(context, client, &salt);
752 salt.saltvalue = *e.val[i].salt;
755 if (e.val[i].salttype)
756 salt.salttype = *e.val[i].salttype;
758 ret = set_paid(paid, context, e.val[i].etype,
761 salt.saltvalue.length,
763 if (e.val[i].salt == NULL)
764 krb5_free_salt(context, salt);
778 static struct pa_info_data *
779 pa_pw_or_afs3_salt(krb5_context context,
780 const krb5_principal client,
782 struct pa_info_data *paid,
783 heim_octet_string *data)
786 if (paid->etype == ENCTYPE_NULL)
788 ret = set_paid(paid, context,
801 krb5_preauthtype type;
802 struct pa_info_data *(*salt_info)(krb5_context,
803 const krb5_principal,
805 struct pa_info_data *,
806 heim_octet_string *);
809 static struct pa_info pa_prefs[] = {
810 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
811 { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
812 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
813 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
817 find_pa_data(const METHOD_DATA *md, int type)
820 for (i = 0; i < md->len; i++)
821 if (md->val[i].padata_type == type)
826 static struct pa_info_data *
827 process_pa_info(krb5_context context,
828 const krb5_principal client,
830 struct pa_info_data *paid,
833 struct pa_info_data *p = NULL;
836 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
837 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
840 paid->salt.salttype = pa_prefs[i].type;
841 p = (*pa_prefs[i].salt_info)(context, client, asreq,
842 paid, &pa->padata_value);
847 static krb5_error_code
848 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
849 krb5_enctype etype, krb5_keyblock *key)
855 EncryptedData encdata;
861 krb5_us_timeofday (context, &p.patimestamp, &usec);
865 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
869 krb5_abortx(context, "internal error in ASN.1 encoder");
871 ret = krb5_crypto_init(context, key, 0, &crypto);
876 ret = krb5_encrypt_EncryptedData(context,
878 KRB5_KU_PA_ENC_TIMESTAMP,
884 krb5_crypto_destroy(context, crypto);
888 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
889 free_EncryptedData(&encdata);
893 krb5_abortx(context, "internal error in ASN.1 encoder");
895 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
901 static krb5_error_code
902 add_enc_ts_padata(krb5_context context,
904 krb5_principal client,
905 krb5_s2k_proc key_proc,
906 krb5_const_pointer keyseed,
907 krb5_enctype *enctypes,
910 krb5_data *s2kparams)
918 /* default to standard salt */
919 ret = krb5_get_pw_salt (context, client, &salt2);
923 enctypes = context->etypes;
925 for (ep = enctypes; *ep != ETYPE_NULL; ep++)
929 for (i = 0; i < netypes; ++i) {
932 ret = (*key_proc)(context, enctypes[i], keyseed,
933 *salt, s2kparams, &key);
936 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
937 krb5_free_keyblock (context, key);
942 krb5_free_salt(context, salt2);
946 static krb5_error_code
947 pa_data_to_md_ts_enc(krb5_context context,
949 const krb5_principal client,
950 krb5_get_init_creds_ctx *ctx,
951 struct pa_info_data *ppaid,
954 if (ctx->key_proc == NULL || ctx->password == NULL)
958 add_enc_ts_padata(context, md, client,
959 ctx->key_proc, ctx->password,
961 &ppaid->salt, ppaid->s2kparams);
965 /* make a v5 salted pa-data */
966 add_enc_ts_padata(context, md, client,
967 ctx->key_proc, ctx->password,
968 a->req_body.etype.val, a->req_body.etype.len,
971 /* make a v4 salted pa-data */
972 salt.salttype = KRB5_PW_SALT;
973 krb5_data_zero(&salt.saltvalue);
974 add_enc_ts_padata(context, md, client,
975 ctx->key_proc, ctx->password,
976 a->req_body.etype.val, a->req_body.etype.len,
982 static krb5_error_code
983 pa_data_to_key_plain(krb5_context context,
984 const krb5_principal client,
985 krb5_get_init_creds_ctx *ctx,
987 krb5_data *s2kparams,
993 ret = (*ctx->key_proc)(context, etype, ctx->password,
994 salt, s2kparams, key);
999 static krb5_error_code
1000 pa_data_to_md_pkinit(krb5_context context,
1002 const krb5_principal client,
1003 krb5_get_init_creds_ctx *ctx,
1006 if (ctx->pk_init_ctx == NULL)
1009 return _krb5_pk_mk_padata(context,
1015 krb5_set_error_string(context, "no support for PKINIT compiled in");
1020 static krb5_error_code
1021 pa_data_add_pac_request(krb5_context context,
1022 krb5_get_init_creds_ctx *ctx,
1026 krb5_error_code ret;
1030 switch (ctx->req_pac) {
1031 case KRB5_PA_PAC_DONT_CARE:
1032 return 0; /* don't bother */
1033 case KRB5_PA_PAC_REQ_TRUE:
1034 req.include_pac = 1;
1036 case KRB5_PA_PAC_REQ_FALSE:
1037 req.include_pac = 0;
1040 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1045 krb5_abortx(context, "internal error in ASN.1 encoder");
1047 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1055 * Assumes caller always will free `out_md', even on error.
1058 static krb5_error_code
1059 process_pa_data_to_md(krb5_context context,
1060 const krb5_creds *creds,
1062 krb5_get_init_creds_ctx *ctx,
1064 METHOD_DATA **out_md,
1065 krb5_prompter_fct prompter,
1066 void *prompter_data)
1068 krb5_error_code ret;
1071 if (*out_md == NULL) {
1072 krb5_set_error_string(context, "malloc: out of memory");
1076 (*out_md)->val = NULL;
1078 if (in_md->len != 0) {
1079 struct pa_info_data paid, *ppaid;
1081 memset(&paid, 0, sizeof(paid));
1083 paid.etype = ENCTYPE_NULL;
1084 ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
1086 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1088 free_paid(context, ppaid);
1091 pa_data_add_pac_request(context, ctx, *out_md);
1092 ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
1096 if ((*out_md)->len == 0) {
1104 static krb5_error_code
1105 process_pa_data_to_key(krb5_context context,
1106 krb5_get_init_creds_ctx *ctx,
1110 const krb5_krbhst_info *hi,
1111 krb5_keyblock **key)
1113 struct pa_info_data paid, *ppaid = NULL;
1114 krb5_error_code ret;
1118 memset(&paid, 0, sizeof(paid));
1120 etype = rep->kdc_rep.enc_part.etype;
1122 if (rep->kdc_rep.padata) {
1124 ppaid = process_pa_info(context, creds->client, a, &paid,
1125 rep->kdc_rep.padata);
1127 if (ppaid == NULL) {
1128 ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1132 paid.s2kparams = NULL;
1136 if (rep->kdc_rep.padata) {
1138 pa = krb5_find_padata(rep->kdc_rep.padata->val,
1139 rep->kdc_rep.padata->len,
1140 KRB5_PADATA_PK_AS_REP,
1144 pa = krb5_find_padata(rep->kdc_rep.padata->val,
1145 rep->kdc_rep.padata->len,
1146 KRB5_PADATA_PK_AS_REP_19,
1150 if (pa && ctx->pk_init_ctx) {
1152 ret = _krb5_pk_rd_pa_reply(context,
1162 krb5_set_error_string(context, "no support for PKINIT compiled in");
1165 } else if (ctx->password)
1166 ret = pa_data_to_key_plain(context, creds->client, ctx,
1167 paid.salt, paid.s2kparams, etype, key);
1169 krb5_set_error_string(context, "No usable pa data type");
1173 free_paid(context, &paid);
1177 static krb5_error_code
1178 init_cred_loop(krb5_context context,
1179 const krb5_get_init_creds_opt *init_cred_opts,
1180 const krb5_prompter_fct prompter,
1181 void *prompter_data,
1182 krb5_get_init_creds_ctx *ctx,
1184 krb5_kdc_rep *ret_as_reply)
1186 krb5_error_code ret;
1192 int send_to_kdc_flags = 0;
1193 krb5_krbhst_info *hi = NULL;
1196 memset(&md, 0, sizeof(md));
1197 memset(&rep, 0, sizeof(rep));
1200 memset(ret_as_reply, 0, sizeof(*ret_as_reply));
1202 ret = init_creds_init_as_req(context, ctx->flags, creds,
1203 ctx->addrs, ctx->etypes, &ctx->as_req);
1207 /* Set a new nonce. */
1208 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1209 ctx->nonce &= 0xffffffff;
1210 /* XXX these just needs to be the same when using Windows PK-INIT */
1211 ctx->pk_nonce = ctx->nonce;
1214 * Increase counter when we want other pre-auth types then
1215 * KRB5_PA_ENC_TIMESTAMP.
1217 #define MAX_PA_COUNTER 3
1219 ctx->pa_counter = 0;
1220 while (ctx->pa_counter < MAX_PA_COUNTER) {
1224 if (ctx->as_req.padata) {
1225 free_METHOD_DATA(ctx->as_req.padata);
1226 free(ctx->as_req.padata);
1227 ctx->as_req.padata = NULL;
1230 /* Set a new nonce. */
1231 ctx->as_req.req_body.nonce = ctx->nonce;
1233 /* fill_in_md_data */
1234 ret = process_pa_data_to_md(context, creds, &ctx->as_req, ctx,
1235 &md, &ctx->as_req.padata,
1236 prompter, prompter_data);
1240 krb5_data_free(&ctx->req_buffer);
1242 ASN1_MALLOC_ENCODE(AS_REQ,
1243 ctx->req_buffer.data, ctx->req_buffer.length,
1244 &ctx->as_req, &len, ret);
1247 if(len != ctx->req_buffer.length)
1248 krb5_abortx(context, "internal error in ASN.1 encoder");
1250 ret = krb5_sendto_kdc_flags (context, &ctx->req_buffer,
1251 &creds->client->realm, &resp,
1256 memset (&rep, 0, sizeof(rep));
1257 ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
1259 krb5_data_free(&resp);
1260 krb5_clear_error_string(context);
1263 /* let's try to parse it as a KRB-ERROR */
1266 ret = krb5_rd_error(context, &resp, &error);
1267 if(ret && resp.data && ((char*)resp.data)[0] == 4)
1268 ret = KRB5KRB_AP_ERR_V4_REPLY;
1269 krb5_data_free(&resp);
1273 ret = krb5_error_from_rd_error(context, &error, creds);
1276 * If no preauth was set and KDC requires it, give it one
1280 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1281 free_METHOD_DATA(&md);
1282 memset(&md, 0, sizeof(md));
1285 ret = decode_METHOD_DATA(error.e_data->data,
1286 error.e_data->length,
1290 krb5_set_error_string(context,
1291 "failed to decode METHOD DATA");
1293 /* XXX guess what the server want here add add md */
1295 krb5_free_error_contents(context, &error);
1298 } else if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1299 if (send_to_kdc_flags & KRB5_KRBHST_FLAGS_LARGE_MSG) {
1303 krb5_free_error_contents(context, &error);
1306 krb5_free_error_contents(context, &error);
1307 send_to_kdc_flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
1312 krb5_free_error_contents(context, &error);
1319 krb5_keyblock *key = NULL;
1321 ret = process_pa_data_to_key(context, ctx, creds,
1322 &ctx->as_req, &rep, hi, &key);
1326 ret = _krb5_extract_ticket(context,
1331 KRB5_KU_AS_REP_ENC_PART,
1335 ctx->flags.b.request_anonymous,
1338 krb5_free_keyblock(context, key);
1341 krb5_data_free(&ctx->req_buffer);
1342 free_METHOD_DATA(&md);
1343 memset(&md, 0, sizeof(md));
1345 if (ret == 0 && ret_as_reply)
1346 *ret_as_reply = rep;
1348 krb5_free_kdc_rep (context, &rep);
1352 krb5_error_code KRB5_LIB_FUNCTION
1353 krb5_get_init_creds(krb5_context context,
1355 krb5_principal client,
1356 krb5_prompter_fct prompter,
1358 krb5_deltat start_time,
1359 const char *in_tkt_service,
1360 krb5_get_init_creds_opt *options)
1362 krb5_get_init_creds_ctx ctx;
1363 krb5_kdc_rep kdc_reply;
1364 krb5_error_code ret;
1368 memset(&kdc_reply, 0, sizeof(kdc_reply));
1370 ret = get_init_creds_common(context, creds, client, start_time,
1371 in_tkt_service, options, &ctx);
1377 memset(&kdc_reply, 0, sizeof(kdc_reply));
1379 ret = init_cred_loop(context,
1391 case KRB5KDC_ERR_KEY_EXPIRED :
1392 /* try to avoid recursion */
1394 /* don't try to change password where then where none */
1395 if (prompter == NULL || ctx.password == NULL)
1398 krb5_clear_error_string (context);
1400 if (ctx.in_tkt_service != NULL
1401 && strcmp (ctx.in_tkt_service, "kadmin/changepw") == 0)
1404 ret = change_password (context,
1422 print_expire (context,
1423 krb5_principal_get_realm (context, ctx.cred.client),
1429 memset (buf, 0, sizeof(buf));
1430 free_init_creds_ctx(context, &ctx);
1431 krb5_free_kdc_rep (context, &kdc_reply);
1435 krb5_free_cred_contents (context, &ctx.cred);
1440 krb5_error_code KRB5_LIB_FUNCTION
1441 krb5_get_init_creds_password(krb5_context context,
1443 krb5_principal client,
1444 const char *password,
1445 krb5_prompter_fct prompter,
1447 krb5_deltat start_time,
1448 const char *in_tkt_service,
1449 krb5_get_init_creds_opt *in_options)
1451 krb5_get_init_creds_opt *options;
1453 krb5_error_code ret;
1455 if (in_options == NULL)
1456 ret = krb5_get_init_creds_opt_alloc(context, &options);
1458 ret = _krb5_get_init_creds_opt_copy(context, in_options, &options);
1462 if (password == NULL &&
1463 options->opt_private->password == NULL &&
1464 options->opt_private->pk_init_ctx == NULL)
1467 krb5_data password_data;
1470 krb5_unparse_name (context, client, &p);
1471 asprintf (&q, "%s's Password: ", p);
1474 password_data.data = buf;
1475 password_data.length = sizeof(buf);
1477 prompt.reply = &password_data;
1478 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1480 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
1483 memset (buf, 0, sizeof(buf));
1484 krb5_get_init_creds_opt_free(options);
1485 ret = KRB5_LIBOS_PWDINTR;
1486 krb5_clear_error_string (context);
1489 password = password_data.data;
1492 if (options->opt_private->password == NULL) {
1493 ret = krb5_get_init_creds_opt_set_pa_password(context, options,
1496 krb5_get_init_creds_opt_free(options);
1497 memset(buf, 0, sizeof(buf));
1502 ret = krb5_get_init_creds(context, creds, client, prompter,
1503 data, start_time, in_tkt_service, options);
1504 krb5_get_init_creds_opt_free(options);
1505 memset(buf, 0, sizeof(buf));
1509 static krb5_error_code
1510 init_creds_keyblock_key_proc (krb5_context context,
1513 krb5_const_pointer keyseed,
1514 krb5_keyblock **key)
1516 return krb5_copy_keyblock (context, keyseed, key);
1519 krb5_error_code KRB5_LIB_FUNCTION
1520 krb5_get_init_creds_keyblock(krb5_context context,
1522 krb5_principal client,
1523 krb5_keyblock *keyblock,
1524 krb5_deltat start_time,
1525 const char *in_tkt_service,
1526 krb5_get_init_creds_opt *options)
1528 struct krb5_get_init_creds_ctx ctx;
1529 krb5_error_code ret;
1531 ret = get_init_creds_common(context, creds, client, start_time,
1532 in_tkt_service, options, &ctx);
1536 ret = krb5_get_in_cred (context,
1542 init_creds_keyblock_key_proc,
1549 if (ret == 0 && creds)
1552 krb5_free_cred_contents (context, &ctx.cred);
1555 free_init_creds_ctx(context, &ctx);