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("$Heimdal: init_creds_pw.c 21931 2007-08-27 14:11:55Z lha $"
39 typedef struct krb5_get_init_creds_ctx
{
42 krb5_addresses
*addrs
;
44 krb5_preauthtype
*pre_auth_types
;
45 const char *in_tkt_service
;
54 krb5_s2k_proc key_proc
;
56 krb5_get_init_creds_tristate req_pac
;
58 krb5_pk_init_ctx pk_init_ctx
;
60 } krb5_get_init_creds_ctx
;
62 static krb5_error_code
63 default_s2k_func(krb5_context context
, krb5_enctype type
,
64 krb5_const_pointer keyseed
,
65 krb5_salt salt
, krb5_data
*s2kparms
,
72 password
.data
= rk_UNCONST(keyseed
);
73 password
.length
= strlen(keyseed
);
77 krb5_data_zero(&opaque
);
79 *key
= malloc(sizeof(**key
));
82 ret
= krb5_string_to_key_data_salt_opaque(context
, type
, password
,
92 free_init_creds_ctx(krb5_context context
, krb5_get_init_creds_ctx
*ctx
)
96 if (ctx
->pre_auth_types
)
97 free (ctx
->pre_auth_types
);
98 free_AS_REQ(&ctx
->as_req
);
99 memset(&ctx
->as_req
, 0, sizeof(ctx
->as_req
));
103 get_config_time (krb5_context context
,
110 ret
= krb5_config_get_time (context
, NULL
,
117 ret
= krb5_config_get_time (context
, NULL
,
126 static krb5_error_code
127 init_cred (krb5_context context
,
129 krb5_principal client
,
130 krb5_deltat start_time
,
131 const char *in_tkt_service
,
132 krb5_get_init_creds_opt
*options
)
135 krb5_const_realm client_realm
;
139 krb5_timeofday (context
, &now
);
141 memset (cred
, 0, sizeof(*cred
));
144 krb5_copy_principal(context
, client
, &cred
->client
);
146 ret
= krb5_get_default_principal (context
,
152 client_realm
= krb5_principal_get_realm (context
, cred
->client
);
155 cred
->times
.starttime
= now
+ start_time
;
157 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_TKT_LIFE
)
158 tmp
= options
->tkt_life
;
161 cred
->times
.endtime
= now
+ tmp
;
163 if ((options
->flags
& KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE
) &&
164 options
->renew_life
> 0) {
165 cred
->times
.renew_till
= now
+ options
->renew_life
;
168 if (in_tkt_service
) {
169 krb5_realm server_realm
;
171 ret
= krb5_parse_name (context
, in_tkt_service
, &cred
->server
);
174 server_realm
= strdup (client_realm
);
175 free (*krb5_princ_realm(context
, cred
->server
));
176 krb5_princ_set_realm (context
, cred
->server
, &server_realm
);
178 ret
= krb5_make_principal(context
, &cred
->server
,
179 client_realm
, KRB5_TGS_NAME
, client_realm
,
187 krb5_free_cred_contents (context
, cred
);
192 * Print a message (str) to the user about the expiration in `lr'
196 report_expiration (krb5_context context
,
197 krb5_prompter_fct prompter
,
204 asprintf (&p
, "%s%s", str
, ctime(&now
));
205 (*prompter
) (context
, data
, NULL
, p
, 0, NULL
);
210 * Parse the last_req data and show it to the user if it's interesting
214 print_expire (krb5_context context
,
215 krb5_const_realm realm
,
217 krb5_prompter_fct prompter
,
221 LastReq
*lr
= &rep
->enc_part
.last_req
;
224 krb5_boolean reported
= FALSE
;
226 krb5_timeofday (context
, &sec
);
228 t
= sec
+ get_config_time (context
,
233 for (i
= 0; i
< lr
->len
; ++i
) {
234 if (lr
->val
[i
].lr_value
<= t
) {
235 switch (abs(lr
->val
[i
].lr_type
)) {
237 report_expiration(context
, prompter
, data
,
238 "Your password will expire at ",
239 lr
->val
[i
].lr_value
);
242 case LR_ACCT_EXPTIME
:
243 report_expiration(context
, prompter
, data
,
244 "Your account will expire at ",
245 lr
->val
[i
].lr_value
);
253 && rep
->enc_part
.key_expiration
254 && *rep
->enc_part
.key_expiration
<= t
) {
255 report_expiration(context
, prompter
, data
,
256 "Your password/account will expire at ",
257 *rep
->enc_part
.key_expiration
);
261 static krb5_addresses no_addrs
= { 0, NULL
};
263 static krb5_error_code
264 get_init_creds_common(krb5_context context
,
265 krb5_principal client
,
266 krb5_deltat start_time
,
267 const char *in_tkt_service
,
268 krb5_get_init_creds_opt
*options
,
269 krb5_get_init_creds_ctx
*ctx
)
271 krb5_get_init_creds_opt default_opt
;
273 krb5_enctype
*etypes
;
274 krb5_preauthtype
*pre_auth_types
;
276 memset(ctx
, 0, sizeof(*ctx
));
278 if (options
== NULL
) {
279 krb5_get_init_creds_opt_init (&default_opt
);
280 options
= &default_opt
;
282 _krb5_get_init_creds_opt_free_krb5_error(options
);
285 if (options
->opt_private
) {
286 ctx
->password
= options
->opt_private
->password
;
287 ctx
->key_proc
= options
->opt_private
->key_proc
;
288 ctx
->req_pac
= options
->opt_private
->req_pac
;
289 ctx
->pk_init_ctx
= options
->opt_private
->pk_init_ctx
;
290 ctx
->ic_flags
= options
->opt_private
->flags
;
292 ctx
->req_pac
= KRB5_INIT_CREDS_TRISTATE_UNSET
;
294 if (ctx
->key_proc
== NULL
)
295 ctx
->key_proc
= default_s2k_func
;
297 if (ctx
->ic_flags
& KRB5_INIT_CREDS_CANONICALIZE
)
298 ctx
->flags
.canonicalize
= 1;
300 ctx
->pre_auth_types
= NULL
;
303 ctx
->pre_auth_types
= NULL
;
304 ctx
->in_tkt_service
= in_tkt_service
;
306 ret
= init_cred (context
, &ctx
->cred
, client
, start_time
,
307 in_tkt_service
, options
);
311 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_FORWARDABLE
)
312 ctx
->flags
.forwardable
= options
->forwardable
;
314 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_PROXIABLE
)
315 ctx
->flags
.proxiable
= options
->proxiable
;
318 ctx
->flags
.postdated
= 1;
319 if (ctx
->cred
.times
.renew_till
)
320 ctx
->flags
.renewable
= 1;
321 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST
) {
322 ctx
->addrs
= options
->address_list
;
323 } else if (options
->opt_private
) {
324 switch (options
->opt_private
->addressless
) {
325 case KRB5_INIT_CREDS_TRISTATE_UNSET
:
326 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
327 ctx
->addrs
= &no_addrs
;
332 case KRB5_INIT_CREDS_TRISTATE_FALSE
:
335 case KRB5_INIT_CREDS_TRISTATE_TRUE
:
336 ctx
->addrs
= &no_addrs
;
340 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST
) {
341 etypes
= malloc((options
->etype_list_length
+ 1)
342 * sizeof(krb5_enctype
));
343 if (etypes
== NULL
) {
344 krb5_set_error_string(context
, "malloc: out of memory");
347 memcpy (etypes
, options
->etype_list
,
348 options
->etype_list_length
* sizeof(krb5_enctype
));
349 etypes
[options
->etype_list_length
] = ETYPE_NULL
;
350 ctx
->etypes
= etypes
;
352 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST
) {
353 pre_auth_types
= malloc((options
->preauth_list_length
+ 1)
354 * sizeof(krb5_preauthtype
));
355 if (pre_auth_types
== NULL
) {
356 krb5_set_error_string(context
, "malloc: out of memory");
359 memcpy (pre_auth_types
, options
->preauth_list
,
360 options
->preauth_list_length
* sizeof(krb5_preauthtype
));
361 pre_auth_types
[options
->preauth_list_length
] = KRB5_PADATA_NONE
;
362 ctx
->pre_auth_types
= pre_auth_types
;
364 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_SALT
)
366 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_ANONYMOUS
)
367 ctx
->flags
.request_anonymous
= options
->anonymous
;
371 static krb5_error_code
372 change_password (krb5_context context
,
373 krb5_principal client
,
374 const char *password
,
377 krb5_prompter_fct prompter
,
379 krb5_get_init_creds_opt
*old_options
)
381 krb5_prompt prompts
[2];
384 char buf1
[BUFSIZ
], buf2
[BUFSIZ
];
385 krb5_data password_data
[2];
387 krb5_data result_code_string
;
388 krb5_data result_string
;
390 krb5_get_init_creds_opt options
;
392 memset (&cpw_cred
, 0, sizeof(cpw_cred
));
394 krb5_get_init_creds_opt_init (&options
);
395 krb5_get_init_creds_opt_set_tkt_life (&options
, 60);
396 krb5_get_init_creds_opt_set_forwardable (&options
, FALSE
);
397 krb5_get_init_creds_opt_set_proxiable (&options
, FALSE
);
398 if (old_options
&& old_options
->flags
& KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST
)
399 krb5_get_init_creds_opt_set_preauth_list (&options
,
400 old_options
->preauth_list
,
401 old_options
->preauth_list_length
);
403 krb5_data_zero (&result_code_string
);
404 krb5_data_zero (&result_string
);
406 ret
= krb5_get_init_creds_password (context
,
419 password_data
[0].data
= buf1
;
420 password_data
[0].length
= sizeof(buf1
);
422 prompts
[0].hidden
= 1;
423 prompts
[0].prompt
= "New password: ";
424 prompts
[0].reply
= &password_data
[0];
425 prompts
[0].type
= KRB5_PROMPT_TYPE_NEW_PASSWORD
;
427 password_data
[1].data
= buf2
;
428 password_data
[1].length
= sizeof(buf2
);
430 prompts
[1].hidden
= 1;
431 prompts
[1].prompt
= "Repeat new password: ";
432 prompts
[1].reply
= &password_data
[1];
433 prompts
[1].type
= KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN
;
435 ret
= (*prompter
) (context
, data
, NULL
, "Changing password",
438 memset (buf1
, 0, sizeof(buf1
));
439 memset (buf2
, 0, sizeof(buf2
));
443 if (strcmp (buf1
, buf2
) == 0)
445 memset (buf1
, 0, sizeof(buf1
));
446 memset (buf2
, 0, sizeof(buf2
));
449 ret
= krb5_change_password (context
,
457 asprintf (&p
, "%s: %.*s\n",
458 result_code
? "Error" : "Success",
459 (int)result_string
.length
,
460 result_string
.length
> 0 ? (char*)result_string
.data
: "");
462 ret
= (*prompter
) (context
, data
, NULL
, p
, 0, NULL
);
464 if (result_code
== 0) {
465 strlcpy (newpw
, buf1
, newpw_sz
);
468 krb5_set_error_string (context
, "failed changing password");
473 memset (buf1
, 0, sizeof(buf1
));
474 memset (buf2
, 0, sizeof(buf2
));
475 krb5_data_free (&result_string
);
476 krb5_data_free (&result_code_string
);
477 krb5_free_cred_contents (context
, &cpw_cred
);
481 krb5_error_code KRB5_LIB_FUNCTION
482 krb5_keyblock_key_proc (krb5_context context
,
485 krb5_const_pointer keyseed
,
488 return krb5_copy_keyblock (context
, keyseed
, key
);
491 krb5_error_code KRB5_LIB_FUNCTION
492 krb5_get_init_creds_keytab(krb5_context context
,
494 krb5_principal client
,
496 krb5_deltat start_time
,
497 const char *in_tkt_service
,
498 krb5_get_init_creds_opt
*options
)
500 krb5_get_init_creds_ctx ctx
;
502 krb5_keytab_key_proc_args
*a
;
504 ret
= get_init_creds_common(context
, client
, start_time
,
505 in_tkt_service
, options
, &ctx
);
509 a
= malloc (sizeof(*a
));
511 krb5_set_error_string(context
, "malloc: out of memory");
515 a
->principal
= ctx
.cred
.client
;
518 ret
= krb5_get_in_cred (context
,
519 KDCOptions2int(ctx
.flags
),
524 krb5_keytab_key_proc
,
532 if (ret
== 0 && creds
)
535 krb5_free_cred_contents (context
, &ctx
.cred
);
538 free_init_creds_ctx(context
, &ctx
);
546 static krb5_error_code
547 init_creds_init_as_req (krb5_context context
,
549 const krb5_creds
*creds
,
550 const krb5_addresses
*addrs
,
551 const krb5_enctype
*etypes
,
556 memset(a
, 0, sizeof(*a
));
559 a
->msg_type
= krb_as_req
;
560 a
->req_body
.kdc_options
= opts
;
561 a
->req_body
.cname
= malloc(sizeof(*a
->req_body
.cname
));
562 if (a
->req_body
.cname
== NULL
) {
564 krb5_set_error_string(context
, "malloc: out of memory");
567 a
->req_body
.sname
= malloc(sizeof(*a
->req_body
.sname
));
568 if (a
->req_body
.sname
== NULL
) {
570 krb5_set_error_string(context
, "malloc: out of memory");
574 ret
= _krb5_principal2principalname (a
->req_body
.cname
, creds
->client
);
577 ret
= copy_Realm(&creds
->client
->realm
, &a
->req_body
.realm
);
581 ret
= _krb5_principal2principalname (a
->req_body
.sname
, creds
->server
);
585 if(creds
->times
.starttime
) {
586 a
->req_body
.from
= malloc(sizeof(*a
->req_body
.from
));
587 if (a
->req_body
.from
== NULL
) {
589 krb5_set_error_string(context
, "malloc: out of memory");
592 *a
->req_body
.from
= creds
->times
.starttime
;
594 if(creds
->times
.endtime
){
595 ALLOC(a
->req_body
.till
, 1);
596 *a
->req_body
.till
= creds
->times
.endtime
;
598 if(creds
->times
.renew_till
){
599 a
->req_body
.rtime
= malloc(sizeof(*a
->req_body
.rtime
));
600 if (a
->req_body
.rtime
== NULL
) {
602 krb5_set_error_string(context
, "malloc: out of memory");
605 *a
->req_body
.rtime
= creds
->times
.renew_till
;
607 a
->req_body
.nonce
= 0;
608 ret
= krb5_init_etype (context
,
609 &a
->req_body
.etype
.len
,
610 &a
->req_body
.etype
.val
,
616 * This means no addresses
619 if (addrs
&& addrs
->len
== 0) {
620 a
->req_body
.addresses
= NULL
;
622 a
->req_body
.addresses
= malloc(sizeof(*a
->req_body
.addresses
));
623 if (a
->req_body
.addresses
== NULL
) {
625 krb5_set_error_string(context
, "malloc: out of memory");
630 ret
= krb5_copy_addresses(context
, addrs
, a
->req_body
.addresses
);
632 ret
= krb5_get_all_client_addrs (context
, a
->req_body
.addresses
);
633 if(ret
== 0 && a
->req_body
.addresses
->len
== 0) {
634 free(a
->req_body
.addresses
);
635 a
->req_body
.addresses
= NULL
;
642 a
->req_body
.enc_authorization_data
= NULL
;
643 a
->req_body
.additional_tickets
= NULL
;
650 memset(a
, 0, sizeof(*a
));
654 struct pa_info_data
{
657 krb5_data
*s2kparams
;
661 free_paid(krb5_context context
, struct pa_info_data
*ppaid
)
663 krb5_free_salt(context
, ppaid
->salt
);
664 if (ppaid
->s2kparams
)
665 krb5_free_data(context
, ppaid
->s2kparams
);
669 static krb5_error_code
670 set_paid(struct pa_info_data
*paid
, krb5_context context
,
672 krb5_salttype salttype
, void *salt_string
, size_t salt_len
,
673 krb5_data
*s2kparams
)
676 paid
->salt
.salttype
= salttype
;
677 paid
->salt
.saltvalue
.data
= malloc(salt_len
+ 1);
678 if (paid
->salt
.saltvalue
.data
== NULL
) {
679 krb5_clear_error_string(context
);
682 memcpy(paid
->salt
.saltvalue
.data
, salt_string
, salt_len
);
683 ((char *)paid
->salt
.saltvalue
.data
)[salt_len
] = '\0';
684 paid
->salt
.saltvalue
.length
= salt_len
;
688 ret
= krb5_copy_data(context
, s2kparams
, &paid
->s2kparams
);
690 krb5_clear_error_string(context
);
691 krb5_free_salt(context
, paid
->salt
);
695 paid
->s2kparams
= NULL
;
700 static struct pa_info_data
*
701 pa_etype_info2(krb5_context context
,
702 const krb5_principal client
,
704 struct pa_info_data
*paid
,
705 heim_octet_string
*data
)
712 memset(&e
, 0, sizeof(e
));
713 ret
= decode_ETYPE_INFO2(data
->data
, data
->length
, &e
, &sz
);
718 for (j
= 0; j
< asreq
->req_body
.etype
.len
; j
++) {
719 for (i
= 0; i
< e
.len
; i
++) {
720 if (asreq
->req_body
.etype
.val
[j
] == e
.val
[i
].etype
) {
722 if (e
.val
[i
].salt
== NULL
)
723 ret
= krb5_get_pw_salt(context
, client
, &salt
);
725 salt
.saltvalue
.data
= *e
.val
[i
].salt
;
726 salt
.saltvalue
.length
= strlen(*e
.val
[i
].salt
);
730 ret
= set_paid(paid
, context
, e
.val
[i
].etype
,
733 salt
.saltvalue
.length
,
735 if (e
.val
[i
].salt
== NULL
)
736 krb5_free_salt(context
, salt
);
738 free_ETYPE_INFO2(&e
);
745 free_ETYPE_INFO2(&e
);
749 static struct pa_info_data
*
750 pa_etype_info(krb5_context context
,
751 const krb5_principal client
,
753 struct pa_info_data
*paid
,
754 heim_octet_string
*data
)
761 memset(&e
, 0, sizeof(e
));
762 ret
= decode_ETYPE_INFO(data
->data
, data
->length
, &e
, &sz
);
767 for (j
= 0; j
< asreq
->req_body
.etype
.len
; j
++) {
768 for (i
= 0; i
< e
.len
; i
++) {
769 if (asreq
->req_body
.etype
.val
[j
] == e
.val
[i
].etype
) {
771 salt
.salttype
= KRB5_PW_SALT
;
772 if (e
.val
[i
].salt
== NULL
)
773 ret
= krb5_get_pw_salt(context
, client
, &salt
);
775 salt
.saltvalue
= *e
.val
[i
].salt
;
778 if (e
.val
[i
].salttype
)
779 salt
.salttype
= *e
.val
[i
].salttype
;
781 ret
= set_paid(paid
, context
, e
.val
[i
].etype
,
784 salt
.saltvalue
.length
,
786 if (e
.val
[i
].salt
== NULL
)
787 krb5_free_salt(context
, salt
);
801 static struct pa_info_data
*
802 pa_pw_or_afs3_salt(krb5_context context
,
803 const krb5_principal client
,
805 struct pa_info_data
*paid
,
806 heim_octet_string
*data
)
809 if (paid
->etype
== ENCTYPE_NULL
)
811 ret
= set_paid(paid
, context
,
824 krb5_preauthtype type
;
825 struct pa_info_data
*(*salt_info
)(krb5_context
,
826 const krb5_principal
,
828 struct pa_info_data
*,
829 heim_octet_string
*);
832 static struct pa_info pa_prefs
[] = {
833 { KRB5_PADATA_ETYPE_INFO2
, pa_etype_info2
},
834 { KRB5_PADATA_ETYPE_INFO
, pa_etype_info
},
835 { KRB5_PADATA_PW_SALT
, pa_pw_or_afs3_salt
},
836 { KRB5_PADATA_AFS3_SALT
, pa_pw_or_afs3_salt
}
840 find_pa_data(const METHOD_DATA
*md
, int type
)
845 for (i
= 0; i
< md
->len
; i
++)
846 if (md
->val
[i
].padata_type
== type
)
851 static struct pa_info_data
*
852 process_pa_info(krb5_context context
,
853 const krb5_principal client
,
855 struct pa_info_data
*paid
,
858 struct pa_info_data
*p
= NULL
;
861 for (i
= 0; p
== NULL
&& i
< sizeof(pa_prefs
)/sizeof(pa_prefs
[0]); i
++) {
862 PA_DATA
*pa
= find_pa_data(md
, pa_prefs
[i
].type
);
865 paid
->salt
.salttype
= pa_prefs
[i
].type
;
866 p
= (*pa_prefs
[i
].salt_info
)(context
, client
, asreq
,
867 paid
, &pa
->padata_value
);
872 static krb5_error_code
873 make_pa_enc_timestamp(krb5_context context
, METHOD_DATA
*md
,
874 krb5_enctype etype
, krb5_keyblock
*key
)
880 EncryptedData encdata
;
886 krb5_us_timeofday (context
, &p
.patimestamp
, &usec
);
890 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC
, buf
, buf_size
, &p
, &len
, ret
);
894 krb5_abortx(context
, "internal error in ASN.1 encoder");
896 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
901 ret
= krb5_encrypt_EncryptedData(context
,
903 KRB5_KU_PA_ENC_TIMESTAMP
,
909 krb5_crypto_destroy(context
, crypto
);
913 ASN1_MALLOC_ENCODE(EncryptedData
, buf
, buf_size
, &encdata
, &len
, ret
);
914 free_EncryptedData(&encdata
);
918 krb5_abortx(context
, "internal error in ASN.1 encoder");
920 ret
= krb5_padata_add(context
, md
, KRB5_PADATA_ENC_TIMESTAMP
, buf
, len
);
926 static krb5_error_code
927 add_enc_ts_padata(krb5_context context
,
929 krb5_principal client
,
930 krb5_s2k_proc key_proc
,
931 krb5_const_pointer keyseed
,
932 krb5_enctype
*enctypes
,
935 krb5_data
*s2kparams
)
943 /* default to standard salt */
944 ret
= krb5_get_pw_salt (context
, client
, &salt2
);
948 enctypes
= context
->etypes
;
950 for (ep
= enctypes
; *ep
!= ETYPE_NULL
; ep
++)
954 for (i
= 0; i
< netypes
; ++i
) {
957 ret
= (*key_proc
)(context
, enctypes
[i
], keyseed
,
958 *salt
, s2kparams
, &key
);
961 ret
= make_pa_enc_timestamp (context
, md
, enctypes
[i
], key
);
962 krb5_free_keyblock (context
, key
);
967 krb5_free_salt(context
, salt2
);
971 static krb5_error_code
972 pa_data_to_md_ts_enc(krb5_context context
,
974 const krb5_principal client
,
975 krb5_get_init_creds_ctx
*ctx
,
976 struct pa_info_data
*ppaid
,
979 if (ctx
->key_proc
== NULL
|| ctx
->password
== NULL
)
983 add_enc_ts_padata(context
, md
, client
,
984 ctx
->key_proc
, ctx
->password
,
986 &ppaid
->salt
, ppaid
->s2kparams
);
990 /* make a v5 salted pa-data */
991 add_enc_ts_padata(context
, md
, client
,
992 ctx
->key_proc
, ctx
->password
,
993 a
->req_body
.etype
.val
, a
->req_body
.etype
.len
,
996 /* make a v4 salted pa-data */
997 salt
.salttype
= KRB5_PW_SALT
;
998 krb5_data_zero(&salt
.saltvalue
);
999 add_enc_ts_padata(context
, md
, client
,
1000 ctx
->key_proc
, ctx
->password
,
1001 a
->req_body
.etype
.val
, a
->req_body
.etype
.len
,
1007 static krb5_error_code
1008 pa_data_to_key_plain(krb5_context context
,
1009 const krb5_principal client
,
1010 krb5_get_init_creds_ctx
*ctx
,
1012 krb5_data
*s2kparams
,
1014 krb5_keyblock
**key
)
1016 krb5_error_code ret
;
1018 ret
= (*ctx
->key_proc
)(context
, etype
, ctx
->password
,
1019 salt
, s2kparams
, key
);
1024 static krb5_error_code
1025 pa_data_to_md_pkinit(krb5_context context
,
1027 const krb5_principal client
,
1028 krb5_get_init_creds_ctx
*ctx
,
1031 if (ctx
->pk_init_ctx
== NULL
)
1034 return _krb5_pk_mk_padata(context
,
1040 krb5_set_error_string(context
, "no support for PKINIT compiled in");
1045 static krb5_error_code
1046 pa_data_add_pac_request(krb5_context context
,
1047 krb5_get_init_creds_ctx
*ctx
,
1051 krb5_error_code ret
;
1055 switch (ctx
->req_pac
) {
1056 case KRB5_INIT_CREDS_TRISTATE_UNSET
:
1057 return 0; /* don't bother */
1058 case KRB5_INIT_CREDS_TRISTATE_TRUE
:
1059 req
.include_pac
= 1;
1061 case KRB5_INIT_CREDS_TRISTATE_FALSE
:
1062 req
.include_pac
= 0;
1065 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST
, buf
, length
,
1070 krb5_abortx(context
, "internal error in ASN.1 encoder");
1072 ret
= krb5_padata_add(context
, md
, KRB5_PADATA_PA_PAC_REQUEST
, buf
, len
);
1080 * Assumes caller always will free `out_md', even on error.
1083 static krb5_error_code
1084 process_pa_data_to_md(krb5_context context
,
1085 const krb5_creds
*creds
,
1087 krb5_get_init_creds_ctx
*ctx
,
1089 METHOD_DATA
**out_md
,
1090 krb5_prompter_fct prompter
,
1091 void *prompter_data
)
1093 krb5_error_code ret
;
1096 if (*out_md
== NULL
) {
1097 krb5_set_error_string(context
, "malloc: out of memory");
1101 (*out_md
)->val
= NULL
;
1104 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1105 * need to expose our password protecting our PKCS12 key.
1108 if (ctx
->pk_init_ctx
) {
1110 ret
= pa_data_to_md_pkinit(context
, a
, creds
->client
, ctx
, *out_md
);
1114 } else if (in_md
->len
!= 0) {
1115 struct pa_info_data paid
, *ppaid
;
1117 memset(&paid
, 0, sizeof(paid
));
1119 paid
.etype
= ENCTYPE_NULL
;
1120 ppaid
= process_pa_info(context
, creds
->client
, a
, &paid
, in_md
);
1122 pa_data_to_md_ts_enc(context
, a
, creds
->client
, ctx
, ppaid
, *out_md
);
1124 free_paid(context
, ppaid
);
1127 pa_data_add_pac_request(context
, ctx
, *out_md
);
1129 if ((*out_md
)->len
== 0) {
1137 static krb5_error_code
1138 process_pa_data_to_key(krb5_context context
,
1139 krb5_get_init_creds_ctx
*ctx
,
1143 const krb5_krbhst_info
*hi
,
1144 krb5_keyblock
**key
)
1146 struct pa_info_data paid
, *ppaid
= NULL
;
1147 krb5_error_code ret
;
1151 memset(&paid
, 0, sizeof(paid
));
1153 etype
= rep
->kdc_rep
.enc_part
.etype
;
1155 if (rep
->kdc_rep
.padata
) {
1157 ppaid
= process_pa_info(context
, creds
->client
, a
, &paid
,
1158 rep
->kdc_rep
.padata
);
1160 if (ppaid
== NULL
) {
1161 ret
= krb5_get_pw_salt (context
, creds
->client
, &paid
.salt
);
1165 paid
.s2kparams
= NULL
;
1169 if (rep
->kdc_rep
.padata
) {
1171 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
1172 rep
->kdc_rep
.padata
->len
,
1173 KRB5_PADATA_PK_AS_REP
,
1177 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
1178 rep
->kdc_rep
.padata
->len
,
1179 KRB5_PADATA_PK_AS_REP_19
,
1183 if (pa
&& ctx
->pk_init_ctx
) {
1185 ret
= _krb5_pk_rd_pa_reply(context
,
1195 krb5_set_error_string(context
, "no support for PKINIT compiled in");
1198 } else if (ctx
->password
)
1199 ret
= pa_data_to_key_plain(context
, creds
->client
, ctx
,
1200 paid
.salt
, paid
.s2kparams
, etype
, key
);
1202 krb5_set_error_string(context
, "No usable pa data type");
1206 free_paid(context
, &paid
);
1210 static krb5_error_code
1211 init_cred_loop(krb5_context context
,
1212 krb5_get_init_creds_opt
*init_cred_opts
,
1213 const krb5_prompter_fct prompter
,
1214 void *prompter_data
,
1215 krb5_get_init_creds_ctx
*ctx
,
1217 krb5_kdc_rep
*ret_as_reply
)
1219 krb5_error_code ret
;
1225 krb5_krbhst_info
*hi
= NULL
;
1226 krb5_sendto_ctx stctx
= NULL
;
1229 memset(&md
, 0, sizeof(md
));
1230 memset(&rep
, 0, sizeof(rep
));
1232 _krb5_get_init_creds_opt_free_krb5_error(init_cred_opts
);
1235 memset(ret_as_reply
, 0, sizeof(*ret_as_reply
));
1237 ret
= init_creds_init_as_req(context
, ctx
->flags
, creds
,
1238 ctx
->addrs
, ctx
->etypes
, &ctx
->as_req
);
1242 ret
= krb5_sendto_ctx_alloc(context
, &stctx
);
1245 krb5_sendto_ctx_set_func(stctx
, _krb5_kdc_retry
, NULL
);
1247 /* Set a new nonce. */
1248 krb5_generate_random_block (&ctx
->nonce
, sizeof(ctx
->nonce
));
1249 ctx
->nonce
&= 0xffffffff;
1250 /* XXX these just needs to be the same when using Windows PK-INIT */
1251 ctx
->pk_nonce
= ctx
->nonce
;
1254 * Increase counter when we want other pre-auth types then
1255 * KRB5_PA_ENC_TIMESTAMP.
1257 #define MAX_PA_COUNTER 3
1259 ctx
->pa_counter
= 0;
1260 while (ctx
->pa_counter
< MAX_PA_COUNTER
) {
1264 if (ctx
->as_req
.padata
) {
1265 free_METHOD_DATA(ctx
->as_req
.padata
);
1266 free(ctx
->as_req
.padata
);
1267 ctx
->as_req
.padata
= NULL
;
1270 /* Set a new nonce. */
1271 ctx
->as_req
.req_body
.nonce
= ctx
->nonce
;
1273 /* fill_in_md_data */
1274 ret
= process_pa_data_to_md(context
, creds
, &ctx
->as_req
, ctx
,
1275 &md
, &ctx
->as_req
.padata
,
1276 prompter
, prompter_data
);
1280 krb5_data_free(&ctx
->req_buffer
);
1282 ASN1_MALLOC_ENCODE(AS_REQ
,
1283 ctx
->req_buffer
.data
, ctx
->req_buffer
.length
,
1284 &ctx
->as_req
, &len
, ret
);
1287 if(len
!= ctx
->req_buffer
.length
)
1288 krb5_abortx(context
, "internal error in ASN.1 encoder");
1290 ret
= krb5_sendto_context (context
, stctx
, &ctx
->req_buffer
,
1291 creds
->client
->realm
, &resp
);
1295 memset (&rep
, 0, sizeof(rep
));
1296 ret
= decode_AS_REP(resp
.data
, resp
.length
, &rep
.kdc_rep
, &size
);
1298 krb5_data_free(&resp
);
1299 krb5_clear_error_string(context
);
1302 /* let's try to parse it as a KRB-ERROR */
1305 ret
= krb5_rd_error(context
, &resp
, &error
);
1306 if(ret
&& resp
.data
&& ((char*)resp
.data
)[0] == 4)
1307 ret
= KRB5KRB_AP_ERR_V4_REPLY
;
1308 krb5_data_free(&resp
);
1312 ret
= krb5_error_from_rd_error(context
, &error
, creds
);
1315 * If no preauth was set and KDC requires it, give it one
1319 if (ret
== KRB5KDC_ERR_PREAUTH_REQUIRED
) {
1320 free_METHOD_DATA(&md
);
1321 memset(&md
, 0, sizeof(md
));
1324 ret
= decode_METHOD_DATA(error
.e_data
->data
,
1325 error
.e_data
->length
,
1329 krb5_set_error_string(context
,
1330 "failed to decode METHOD DATA");
1332 /* XXX guess what the server want here add add md */
1334 krb5_free_error_contents(context
, &error
);
1338 _krb5_get_init_creds_opt_set_krb5_error(context
,
1344 krb5_free_error_contents(context
, &error
);
1351 krb5_keyblock
*key
= NULL
;
1354 if (ctx
->flags
.request_anonymous
)
1355 flags
|= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
;
1356 if (ctx
->flags
.canonicalize
) {
1357 flags
|= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
;
1358 flags
|= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
;
1359 flags
|= EXTRACT_TICKET_MATCH_REALM
;
1362 ret
= process_pa_data_to_key(context
, ctx
, creds
,
1363 &ctx
->as_req
, &rep
, hi
, &key
);
1367 ret
= _krb5_extract_ticket(context
,
1372 KRB5_KU_AS_REP_ENC_PART
,
1378 krb5_free_keyblock(context
, key
);
1381 * Verify referral data
1383 if ((ctx
->ic_flags
& KRB5_INIT_CREDS_CANONICALIZE
) &&
1384 (ctx
->ic_flags
& KRB5_INIT_CREDS_NO_C_CANON_CHECK
) == 0)
1386 PA_ClientCanonicalized canon
;
1392 pa
= find_pa_data(rep
.kdc_rep
.padata
, KRB5_PADATA_CLIENT_CANONICALIZED
);
1395 krb5_set_error_string(context
, "Client canonicalizion not signed");
1399 ret
= decode_PA_ClientCanonicalized(pa
->padata_value
.data
,
1400 pa
->padata_value
.length
,
1403 krb5_set_error_string(context
, "Failed to decode "
1404 "PA_ClientCanonicalized");
1408 ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames
, data
.data
, data
.length
,
1409 &canon
.names
, &len
, ret
);
1412 if (data
.length
!= len
)
1413 krb5_abortx(context
, "internal asn.1 error");
1415 ret
= krb5_crypto_init(context
, &creds
->session
, 0, &crypto
);
1418 free_PA_ClientCanonicalized(&canon
);
1422 ret
= krb5_verify_checksum(context
, crypto
, KRB5_KU_CANONICALIZED_NAMES
,
1423 data
.data
, data
.length
,
1424 &canon
.canon_checksum
);
1425 krb5_crypto_destroy(context
, crypto
);
1427 free_PA_ClientCanonicalized(&canon
);
1429 krb5_set_error_string(context
, "Failed to verify "
1430 "client canonicalized data");
1436 krb5_sendto_ctx_free(context
, stctx
);
1437 krb5_data_free(&ctx
->req_buffer
);
1438 free_METHOD_DATA(&md
);
1439 memset(&md
, 0, sizeof(md
));
1441 if (ret
== 0 && ret_as_reply
)
1442 *ret_as_reply
= rep
;
1444 krb5_free_kdc_rep (context
, &rep
);
1448 krb5_error_code KRB5_LIB_FUNCTION
1449 krb5_get_init_creds(krb5_context context
,
1451 krb5_principal client
,
1452 krb5_prompter_fct prompter
,
1454 krb5_deltat start_time
,
1455 const char *in_tkt_service
,
1456 krb5_get_init_creds_opt
*options
)
1458 krb5_get_init_creds_ctx ctx
;
1459 krb5_kdc_rep kdc_reply
;
1460 krb5_error_code ret
;
1464 memset(&kdc_reply
, 0, sizeof(kdc_reply
));
1466 ret
= get_init_creds_common(context
, client
, start_time
,
1467 in_tkt_service
, options
, &ctx
);
1473 memset(&kdc_reply
, 0, sizeof(kdc_reply
));
1475 ret
= init_cred_loop(context
,
1487 case KRB5KDC_ERR_KEY_EXPIRED
:
1488 /* try to avoid recursion */
1490 /* don't try to change password where then where none */
1491 if (prompter
== NULL
|| ctx
.password
== NULL
)
1494 krb5_clear_error_string (context
);
1496 if (ctx
.in_tkt_service
!= NULL
1497 && strcmp (ctx
.in_tkt_service
, "kadmin/changepw") == 0)
1500 ret
= change_password (context
,
1518 print_expire (context
,
1519 krb5_principal_get_realm (context
, ctx
.cred
.client
),
1525 memset (buf
, 0, sizeof(buf
));
1526 free_init_creds_ctx(context
, &ctx
);
1527 krb5_free_kdc_rep (context
, &kdc_reply
);
1531 krb5_free_cred_contents (context
, &ctx
.cred
);
1536 krb5_error_code KRB5_LIB_FUNCTION
1537 krb5_get_init_creds_password(krb5_context context
,
1539 krb5_principal client
,
1540 const char *password
,
1541 krb5_prompter_fct prompter
,
1543 krb5_deltat start_time
,
1544 const char *in_tkt_service
,
1545 krb5_get_init_creds_opt
*in_options
)
1547 krb5_get_init_creds_opt
*options
;
1549 krb5_error_code ret
;
1551 if (in_options
== NULL
) {
1552 const char *realm
= krb5_principal_get_realm(context
, client
);
1553 ret
= krb5_get_init_creds_opt_alloc(context
, &options
);
1555 krb5_get_init_creds_opt_set_default_flags(context
,
1560 ret
= _krb5_get_init_creds_opt_copy(context
, in_options
, &options
);
1564 if (password
== NULL
&&
1565 options
->opt_private
->password
== NULL
&&
1566 options
->opt_private
->pk_init_ctx
== NULL
)
1569 krb5_data password_data
;
1572 krb5_unparse_name (context
, client
, &p
);
1573 asprintf (&q
, "%s's Password: ", p
);
1576 password_data
.data
= buf
;
1577 password_data
.length
= sizeof(buf
);
1579 prompt
.reply
= &password_data
;
1580 prompt
.type
= KRB5_PROMPT_TYPE_PASSWORD
;
1582 ret
= (*prompter
) (context
, data
, NULL
, NULL
, 1, &prompt
);
1585 memset (buf
, 0, sizeof(buf
));
1586 krb5_get_init_creds_opt_free(context
, options
);
1587 ret
= KRB5_LIBOS_PWDINTR
;
1588 krb5_clear_error_string (context
);
1591 password
= password_data
.data
;
1594 if (options
->opt_private
->password
== NULL
) {
1595 ret
= krb5_get_init_creds_opt_set_pa_password(context
, options
,
1598 krb5_get_init_creds_opt_free(context
, options
);
1599 memset(buf
, 0, sizeof(buf
));
1604 ret
= krb5_get_init_creds(context
, creds
, client
, prompter
,
1605 data
, start_time
, in_tkt_service
, options
);
1606 krb5_get_init_creds_opt_free(context
, options
);
1607 memset(buf
, 0, sizeof(buf
));
1611 static krb5_error_code
1612 init_creds_keyblock_key_proc (krb5_context context
,
1615 krb5_const_pointer keyseed
,
1616 krb5_keyblock
**key
)
1618 return krb5_copy_keyblock (context
, keyseed
, key
);
1621 krb5_error_code KRB5_LIB_FUNCTION
1622 krb5_get_init_creds_keyblock(krb5_context context
,
1624 krb5_principal client
,
1625 krb5_keyblock
*keyblock
,
1626 krb5_deltat start_time
,
1627 const char *in_tkt_service
,
1628 krb5_get_init_creds_opt
*options
)
1630 struct krb5_get_init_creds_ctx ctx
;
1631 krb5_error_code ret
;
1633 ret
= get_init_creds_common(context
, client
, start_time
,
1634 in_tkt_service
, options
, &ctx
);
1638 ret
= krb5_get_in_cred (context
,
1639 KDCOptions2int(ctx
.flags
),
1644 init_creds_keyblock_key_proc
,
1651 if (ret
== 0 && creds
)
1654 krb5_free_cred_contents (context
, &ctx
.cred
);
1657 free_init_creds_ctx(context
, &ctx
);