1 /*
2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */
3 
4 /*
5  * Copyright 2019 Joyent, Inc.
6  */
7 
8 #include "k5-int.h"
9 #include "com_err.h"
10 #include <admin.h>
11 #include <locale.h>
12 #include <syslog.h>
13 
14 /* Solaris Kerberos:
15  *
16  * Change Password functionality is handled by the libkadm5clnt.so.1 library in
17  * Solaris Kerberos. In order to avoid a circular dependency between that lib
18  * and the kerberos mech lib, we use the #pragma weak compiler directive.
19  * This way, when applications link with the libkadm5clnt.so.1 lib the circular
20  * dependancy between the two libs will be resolved.
21  */
22 
23 #pragma weak kadm5_get_cpw_host_srv_name
24 #pragma weak kadm5_init_with_password
25 #pragma weak kadm5_chpass_principal_util
26 
27 extern kadm5_ret_t kadm5_get_cpw_host_srv_name(krb5_context, const char *,
28 			char **);
29 extern kadm5_ret_t kadm5_init_with_password(char *, char *, char *,
30 			kadm5_config_params *, krb5_ui_4, krb5_ui_4, char **,
31 			void **);
32 extern kadm5_ret_t kadm5_chpass_principal_util(void *, krb5_principal,
33 			char *, char **, char *, unsigned int);
34 
35 /*
36  * Solaris Kerberos:
37  * See the function's definition for the description of this interface.
38  */
39 krb5_error_code __krb5_get_init_creds_password(krb5_context,
40 	krb5_creds *, krb5_principal, char *, krb5_prompter_fct, void *,
41 	krb5_deltat, char *, krb5_get_init_creds_opt *, krb5_kdc_rep **);
42 
43 static krb5_error_code
krb5_get_as_key_password(krb5_context context,krb5_principal client,krb5_enctype etype,krb5_prompter_fct prompter,void * prompter_data,krb5_data * salt,krb5_data * params,krb5_keyblock * as_key,void * gak_data)44 krb5_get_as_key_password(
45     krb5_context context,
46     krb5_principal client,
47     krb5_enctype etype,
48     krb5_prompter_fct prompter,
49     void *prompter_data,
50     krb5_data *salt,
51     krb5_data *params,
52     krb5_keyblock *as_key,
53     void *gak_data)
54 {
55     krb5_data *password;
56     krb5_error_code ret;
57     krb5_data defsalt;
58     char *clientstr;
59     char promptstr[1024];
60     krb5_prompt prompt;
61     krb5_prompt_type prompt_type;
62 
63     password = (krb5_data *) gak_data;
64 
65     /* If there's already a key of the correct etype, we're done.
66        If the etype is wrong, free the existing key, and make
67        a new one.
68 
69        XXX This was the old behavior, and was wrong in hw preauth
70        cases.  Is this new behavior -- always asking -- correct in all
71        cases?  */
72 
73     if (as_key->length) {
74 	if (as_key->enctype != etype) {
75 	    krb5_free_keyblock_contents (context, as_key);
76 	    as_key->length = 0;
77 	}
78     }
79 
80     if (password->data[0] == '\0') {
81 	if (prompter == NULL)
82 		prompter = krb5_prompter_posix; /* Solaris Kerberos */
83 
84 	if ((ret = krb5_unparse_name(context, client, &clientstr)))
85 	  return(ret);
86 
87 	strcpy(promptstr, "Password for ");
88 	strncat(promptstr, clientstr, sizeof(promptstr)-strlen(promptstr)-1);
89 	promptstr[sizeof(promptstr)-1] = '\0';
90 
91 	free(clientstr);
92 
93 	prompt.prompt = promptstr;
94 	prompt.hidden = 1;
95 	prompt.reply = password;
96 	prompt_type = KRB5_PROMPT_TYPE_PASSWORD;
97 
98 	/* PROMPTER_INVOCATION */
99 	krb5int_set_prompt_types(context, &prompt_type);
100 	if ((ret = (((*prompter)(context, prompter_data, NULL, NULL,
101 				1, &prompt))))) {
102 	    krb5int_set_prompt_types(context, 0);
103 	    return(ret);
104 	}
105 	krb5int_set_prompt_types(context, 0);
106     }
107 
108     if ((salt->length == -1 || salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
109 	if ((ret = krb5_principal2salt(context, client, &defsalt)))
110 	    return(ret);
111 
112 	salt = &defsalt;
113     } else {
114 	defsalt.length = 0;
115     }
116 
117     ret = krb5_c_string_to_key_with_params(context, etype, password, salt,
118 					   params->data?params:NULL, as_key);
119 
120     if (defsalt.length)
121 	krb5_xfree(defsalt.data);
122 
123     return(ret);
124 }
125 
126 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_password(krb5_context context,krb5_creds * creds,krb5_principal client,char * password,krb5_prompter_fct prompter,void * data,krb5_deltat start_time,char * in_tkt_service,krb5_get_init_creds_opt * options)127 krb5_get_init_creds_password(krb5_context context,
128 			     krb5_creds *creds,
129 			     krb5_principal client,
130 			     char *password,
131 			     krb5_prompter_fct prompter,
132 			     void *data,
133 			     krb5_deltat start_time,
134 			     char *in_tkt_service,
135 			     krb5_get_init_creds_opt *options)
136 {
137 	/*
138 	 * Solaris Kerberos:
139 	 * We call our own private function that returns the as_reply back to
140 	 * the caller.  This structure contains information, such as
141 	 * key-expiration and last-req fields.  Entities such as pam_krb5 can
142 	 * use this information to provide account/password expiration warnings.
143 	 * The original "prompter" interface is not granular enough for PAM,
144 	 * as it will perform all passes w/o coordination with other modules.
145 	 */
146 	return (__krb5_get_init_creds_password(context, creds, client, password,
147 		prompter, data, start_time, in_tkt_service, options, NULL));
148 }
149 
150 /*
151  * Solaris Kerberos:
152  * See krb5_get_init_creds_password()'s comments for the justification of this
153  * private function.  Caller must free ptr_as_reply if non-NULL.
154  */
155 krb5_error_code KRB5_CALLCONV
__krb5_get_init_creds_password(krb5_context context,krb5_creds * creds,krb5_principal client,char * password,krb5_prompter_fct prompter,void * data,krb5_deltat start_time,char * in_tkt_service,krb5_get_init_creds_opt * options,krb5_kdc_rep ** ptr_as_reply)156 __krb5_get_init_creds_password(
157      krb5_context context,
158      krb5_creds *creds,
159      krb5_principal client,
160      char *password,
161      krb5_prompter_fct prompter,
162      void *data,
163      krb5_deltat start_time,
164      char *in_tkt_service,
165      krb5_get_init_creds_opt *options,
166      krb5_kdc_rep **ptr_as_reply)
167 {
168    krb5_error_code ret, ret2;
169    int use_master;
170    krb5_kdc_rep *as_reply;
171    int tries;
172    krb5_creds chpw_creds;
173    krb5_data pw0, pw1;
174    char banner[1024], pw0array[1024], pw1array[1024];
175    krb5_prompt prompt[2];
176    krb5_prompt_type prompt_types[sizeof(prompt)/sizeof(prompt[0])];
177    krb5_gic_opt_ext *opte = NULL;
178    krb5_gic_opt_ext *chpw_opte = NULL;
179 
180    char admin_realm[1024], *cpw_service=NULL, *princ_str=NULL;
181    kadm5_config_params  params;
182    void *server_handle;
183    const char *err_msg_1 = NULL;
184 
185    use_master = 0;
186    as_reply = NULL;
187    memset(&chpw_creds, 0, sizeof(chpw_creds));
188 
189    pw0.data = pw0array;
190 
191    if (password && password[0]) {
192       if ((pw0.length = strlen(password)) > sizeof(pw0array)) {
193 	 ret = EINVAL;
194 	 goto cleanup;
195       }
196       strcpy(pw0.data, password);
197    } else {
198       pw0.data[0] = '\0';
199       pw0.length = sizeof(pw0array);
200    }
201 
202    pw1.data = pw1array;
203    pw1.data[0] = '\0';
204    pw1.length = sizeof(pw1array);
205 
206    ret = krb5int_gic_opt_to_opte(context, options, &opte, 1,
207 				 "krb5_get_init_creds_password");
208    if (ret)
209       goto cleanup;
210 
211    /* first try: get the requested tkt from any kdc */
212 
213    ret = krb5_get_init_creds(context, creds, client, prompter, data,
214 			     start_time, in_tkt_service, opte,
215 			     krb5_get_as_key_password, (void *) &pw0,
216 			     &use_master, &as_reply);
217    /* check for success */
218 
219    if (ret == 0)
220       goto cleanup;
221 
222    /* If all the kdc's are unavailable, or if the error was due to a
223       user interrupt, or preauth errored out, fail */
224 
225    if ((ret == KRB5_KDC_UNREACH) ||
226        (ret == KRB5_PREAUTH_FAILED) ||
227        (ret == KRB5_LIBOS_PWDINTR) ||
228        (ret == KRB5_REALM_CANT_RESOLVE))
229       goto cleanup;
230 
231    /* if the reply did not come from the master kdc, try again with
232       the master kdc */
233 
234    if (!use_master) {
235       use_master = 1;
236 
237       if (as_reply) {
238 	  krb5_free_kdc_rep( context, as_reply);
239 	  as_reply = NULL;
240       }
241 
242       err_msg_1 = krb5_get_error_message(context, ret);
243 
244       ret2 = krb5_get_init_creds(context, creds, client, prompter, data,
245 				 start_time, in_tkt_service, opte,
246 				 krb5_get_as_key_password, (void *) &pw0,
247 				 &use_master, &as_reply);
248 
249       if (ret2 == 0) {
250 	 ret = 0;
251 	 goto cleanup;
252       }
253 
254       /* if the master is unreachable, return the error from the
255 	 slave we were able to contact or reset the use_master flag */
256 
257       if ((ret2 != KRB5_KDC_UNREACH) &&
258 	(ret2 != KRB5_REALM_CANT_RESOLVE) &&
259 	(ret2 != KRB5_REALM_UNKNOWN)) {
260 	ret = ret2;
261       } else {
262 	use_master = 0;
263 	/* Solaris - if 2nd try failed, reset 1st err msg */
264 	if (ret2 && err_msg_1) {
265 	  krb5_set_error_message(context, ret, err_msg_1);
266 	}
267       }
268    }
269 
270 /* Solaris Kerberos: 163 resync */
271 /* #ifdef USE_LOGIN_LIBRARY */
272 	if (ret == KRB5KDC_ERR_KEY_EXP)
273 		goto cleanup;	/* Login library will deal appropriately with this error */
274 /* #endif */
275 
276    /* at this point, we have an error from the master.  if the error
277       is not password expired, or if it is but there's no prompter,
278       return this error */
279 
280    if ((ret != KRB5KDC_ERR_KEY_EXP) ||
281        (prompter == NULL))
282       goto cleanup;
283 
284     /* historically the default has been to prompt for password change.
285      * if the change password prompt option has not been set, we continue
286      * to prompt.  Prompting is only disabled if the option has been set
287      * and the value has been set to false.
288      */
289     if (!(options->flags & KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT))
290 	goto cleanup;
291 
292     /* ok, we have an expired password.  Give the user a few chances
293       to change it */
294 
295 
296    /*
297     * Solaris Kerberos:
298     * Get the correct change password service principal name to use.
299     * This is necessary because SEAM based admin servers require
300     * a slightly different service principal name than MIT/MS servers.
301     */
302 
303    memset((char *) &params, 0, sizeof (params));
304 
305    snprintf(admin_realm, sizeof (admin_realm),
306 	krb5_princ_realm(context, client)->data);
307    params.mask |= KADM5_CONFIG_REALM;
308    params.realm = admin_realm;
309 
310    ret=kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service);
311 
312    if (ret != KADM5_OK) {
313 	syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
314 	    "Kerberos mechanism library: Unable to get change password "
315 	    "service name for realm %s\n"), admin_realm);
316 	goto cleanup;
317    } else {
318 	ret=0;
319    }
320 
321    /* extract the string version of the principal */
322    if ((ret = krb5_unparse_name(context, client, &princ_str)))
323 	goto cleanup;
324 
325    ret = kadm5_init_with_password(princ_str, pw0array, cpw_service,
326 	&params, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, NULL,
327 	&server_handle);
328 
329    if (ret != 0) {
330 	goto cleanup;
331    }
332 
333    prompt[0].prompt = "Enter new password";
334    prompt[0].hidden = 1;
335    prompt[0].reply = &pw0;
336    prompt_types[0] = KRB5_PROMPT_TYPE_NEW_PASSWORD;
337 
338    prompt[1].prompt = "Enter it again";
339    prompt[1].hidden = 1;
340    prompt[1].reply = &pw1;
341    prompt_types[1] = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
342 
343    strcpy(banner, "Password expired.  You must change it now.");
344 
345    for (tries = 3; tries; tries--) {
346       pw0.length = sizeof(pw0array);
347       pw1.length = sizeof(pw1array);
348 
349       /* PROMPTER_INVOCATION */
350       krb5int_set_prompt_types(context, prompt_types);
351       if ((ret = ((*prompter)(context, data, 0, banner,
352 			      sizeof(prompt)/sizeof(prompt[0]), prompt))))
353 	 goto cleanup;
354       krb5int_set_prompt_types(context, 0);
355 
356 
357       if (strcmp(pw0.data, pw1.data) != 0) {
358 	 ret = KRB5_LIBOS_BADPWDMATCH;
359 	 sprintf(banner, "%s.  Please try again.", error_message(ret));
360       } else if (pw0.length == 0) {
361 	 ret = KRB5_CHPW_PWDNULL;
362 	 sprintf(banner, "%s.  Please try again.", error_message(ret));
363       } else {
364 	 int result_code;
365 	 krb5_data code_string;
366 	 krb5_data result_string;
367 
368 	 if ((ret = krb5_change_password(context, &chpw_creds, pw0array,
369 					 &result_code, &code_string,
370 					 &result_string)))
371 	    goto cleanup;
372 
373 	 /* the change succeeded.  go on */
374 
375 	 if (result_code == 0) {
376 	    krb5_xfree(result_string.data);
377 	    break;
378 	 }
379 
380 	 /* set this in case the retry loop falls through */
381 
382 	 ret = KRB5_CHPW_FAIL;
383 
384 	 if (result_code != KRB5_KPASSWD_SOFTERROR) {
385 	    krb5_xfree(result_string.data);
386 	    goto cleanup;
387 	 }
388 
389 	 /* the error was soft, so try again */
390 
391 	 /* 100 is I happen to know that no code_string will be longer
392 	    than 100 chars */
393 
394 	 if (result_string.length > (sizeof(banner)-100))
395 	    result_string.length = sizeof(banner)-100;
396 
397 	 sprintf(banner, "%.*s%s%.*s.  Please try again.\n",
398 		 (int) code_string.length, code_string.data,
399 		 result_string.length ? ": " : "",
400 		 (int) result_string.length,
401 		 result_string.data ? result_string.data : "");
402 
403 	 krb5_xfree(code_string.data);
404 	 krb5_xfree(result_string.data);
405       }
406    }
407 
408    if (ret)
409       goto cleanup;
410 
411    /* the password change was successful.  Get an initial ticket
412       from the master.  this is the last try.  the return from this
413       is final.  */
414 
415    ret = krb5_get_init_creds(context, creds, client, prompter, data,
416 			     start_time, in_tkt_service, opte,
417 			     krb5_get_as_key_password, (void *) &pw0,
418 			     &use_master, &as_reply);
419 
420 cleanup:
421    if (err_msg_1)
422      free((void *)err_msg_1);
423 
424    krb5int_set_prompt_types(context, 0);
425    /* if getting the password was successful, then check to see if the
426       password is about to expire, and warn if so */
427 
428    if (ret == 0) {
429       krb5_timestamp now;
430       krb5_last_req_entry **last_req;
431       int hours;
432 
433       /* XXX 7 days should be configurable.  This is all pretty ad hoc,
434 	 and could probably be improved if I was willing to screw around
435 	 with timezones, etc. */
436 
437       if (prompter &&
438 	  (in_tkt_service && cpw_service &&
439 	   (strcmp(in_tkt_service, cpw_service) != 0)) &&
440 	  ((ret = krb5_timeofday(context, &now)) == 0) &&
441 	  as_reply->enc_part2->key_exp &&
442 	  ((hours = ((as_reply->enc_part2->key_exp-now)/(60*60))) <= 7*24) &&
443 	  (hours >= 0)) {
444 	 if (hours < 1)
445 	    sprintf(banner,
446 		    "Warning: Your password will expire in less than one hour.");
447 	 else if (hours <= 48)
448 	    sprintf(banner, "Warning: Your password will expire in %d hour%s.",
449 		    hours, (hours == 1)?"":"s");
450 	 else
451 	    sprintf(banner, "Warning: Your password will expire in %d days.",
452 		    hours/24);
453 
454 	 /* ignore an error here */
455          /* PROMPTER_INVOCATION */
456 	 (*prompter)(context, data, 0, banner, 0, 0);
457       } else if (prompter &&
458 		 (!in_tkt_service ||
459 		  (strcmp(in_tkt_service, "kadmin/changepw") != 0)) &&
460 		 as_reply->enc_part2 && as_reply->enc_part2->last_req) {
461 	 /*
462 	  * Check the last_req fields
463 	  */
464 
465 	 for (last_req = as_reply->enc_part2->last_req; *last_req; last_req++)
466 	    if ((*last_req)->lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
467 		(*last_req)->lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
468 	       krb5_deltat delta;
469 	       char ts[256];
470 
471 	       if ((ret = krb5_timeofday(context, &now)))
472 		  break;
473 
474 	       if ((ret = krb5_timestamp_to_string((*last_req)->value,
475 						   ts, sizeof(ts))))
476 		  break;
477 
478 	       delta = (*last_req)->value - now;
479 
480 	       if (delta < 3600)
481 		  sprintf(banner,
482 		    "Warning: Your password will expire in less than one "
483 		     "hour on %s", ts);
484 	       else if (delta < 86400*2)
485 		  sprintf(banner,
486 		     "Warning: Your password will expire in %d hour%s on %s",
487 		     delta / 3600, delta < 7200 ? "" : "s", ts);
488 	       else
489 		  sprintf(banner,
490 		     "Warning: Your password will expire in %d days on %s",
491 		     delta / 86400, ts);
492 	       /* ignore an error here */
493 	       /* PROMPTER_INVOCATION */
494 	       (*prompter)(context, data, 0, banner, 0, 0);
495 	    }
496       }
497    }
498 
499    free(cpw_service);
500    free(princ_str);
501    if (opte && krb5_gic_opt_is_shadowed(opte))
502       krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
503    memset(pw0array, 0, sizeof(pw0array));
504    memset(pw1array, 0, sizeof(pw1array));
505    krb5_free_cred_contents(context, &chpw_creds);
506    /*
507     * Solaris Kerberos:
508     * Argument, ptr_as_reply, being returned to caller if success and non-NULL.
509     */
510    if (as_reply != NULL) {
511 	if (ptr_as_reply == NULL)
512       	   krb5_free_kdc_rep(context, as_reply);
513 	else
514 	   *ptr_as_reply = as_reply;
515    }
516 
517    return(ret);
518 }
krb5int_populate_gic_opt(krb5_context context,krb5_gic_opt_ext ** opte,krb5_flags options,krb5_address * const * addrs,krb5_enctype * ktypes,krb5_preauthtype * pre_auth_types,krb5_creds * creds)519 krb5_error_code krb5int_populate_gic_opt (
520     krb5_context context, krb5_gic_opt_ext **opte,
521     krb5_flags options, krb5_address * const *addrs, krb5_enctype *ktypes,
522     krb5_preauthtype *pre_auth_types, krb5_creds *creds)
523 {
524   int i;
525   krb5_int32 starttime;
526   krb5_get_init_creds_opt opt;
527 
528 
529     krb5_get_init_creds_opt_init(&opt);
530     if (addrs)
531       krb5_get_init_creds_opt_set_address_list(&opt, (krb5_address **) addrs);
532     if (ktypes) {
533 	for (i=0; ktypes[i]; i++);
534 	if (i)
535 	    krb5_get_init_creds_opt_set_etype_list(&opt, ktypes, i);
536     }
537     if (pre_auth_types) {
538 	for (i=0; pre_auth_types[i]; i++);
539 	if (i)
540 	    krb5_get_init_creds_opt_set_preauth_list(&opt, pre_auth_types, i);
541     }
542     if (options&KDC_OPT_FORWARDABLE)
543 	krb5_get_init_creds_opt_set_forwardable(&opt, 1);
544     else krb5_get_init_creds_opt_set_forwardable(&opt, 0);
545     if (options&KDC_OPT_PROXIABLE)
546 	krb5_get_init_creds_opt_set_proxiable(&opt, 1);
547     else krb5_get_init_creds_opt_set_proxiable(&opt, 0);
548     if (creds && creds->times.endtime) {
549         krb5_timeofday(context, &starttime);
550         if (creds->times.starttime) starttime = creds->times.starttime;
551         krb5_get_init_creds_opt_set_tkt_life(&opt, creds->times.endtime - starttime);
552     }
553     return krb5int_gic_opt_to_opte(context, &opt, opte, 0,
554 				   "krb5int_populate_gic_opt");
555 }
556 
557 /*
558   Rewrites get_in_tkt in terms of newer get_init_creds API.
559  Attempts to get an initial ticket for creds->client to use server
560  creds->server, (realm is taken from creds->client), with options
561  options, and using creds->times.starttime, creds->times.endtime,
562  creds->times.renew_till as from, till, and rtime.
563  creds->times.renew_till is ignored unless the RENEWABLE option is requested.
564 
565  If addrs is non-NULL, it is used for the addresses requested.  If it is
566  null, the system standard addresses are used.
567 
568  If password is non-NULL, it is converted using the cryptosystem entry
569  point for a string conversion routine, seeded with the client's name.
570  If password is passed as NULL, the password is read from the terminal,
571  and then converted into a key.
572 
573  A succesful call will place the ticket in the credentials cache ccache.
574 
575  returns system errors, encryption errors
576  */
577 krb5_error_code KRB5_CALLCONV
krb5_get_in_tkt_with_password(krb5_context context,krb5_flags options,krb5_address * const * addrs,krb5_enctype * ktypes,krb5_preauthtype * pre_auth_types,const char * password,krb5_ccache ccache,krb5_creds * creds,krb5_kdc_rep ** ret_as_reply)578 krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options,
579 			      krb5_address *const *addrs, krb5_enctype *ktypes,
580 			      krb5_preauthtype *pre_auth_types,
581 			      const char *password, krb5_ccache ccache,
582 			      krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
583 {
584     krb5_error_code retval;
585     krb5_data pw0;
586     char pw0array[1024];
587     char * server;
588     krb5_principal server_princ, client_princ;
589     int use_master = 0;
590     krb5_gic_opt_ext *opte = NULL;
591 
592     pw0array[0] = '\0';
593     pw0.data = pw0array;
594     if (password) {
595 	pw0.length = strlen(password);
596 	if (pw0.length > sizeof(pw0array))
597 	    return EINVAL;
598 	strncpy(pw0.data, password, sizeof(pw0array));
599 	if (pw0.length == 0)
600 	    pw0.length = sizeof(pw0array);
601     } else {
602 	pw0.length = sizeof(pw0array);
603     }
604     retval = krb5int_populate_gic_opt(context, &opte,
605 				      options, addrs, ktypes,
606 				      pre_auth_types, creds);
607     if (retval)
608       return (retval);
609     retval = krb5_unparse_name( context, creds->server, &server);
610     if (retval) {
611       return (retval);
612       krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
613     }
614     server_princ = creds->server;
615     client_princ = creds->client;
616         retval = krb5_get_init_creds (context,
617 					   creds, creds->client,
618 					   krb5_prompter_posix,  NULL,
619 					   0, server, opte,
620 				      krb5_get_as_key_password, &pw0,
621 				      &use_master, ret_as_reply);
622 	  krb5_free_unparsed_name( context, server);
623 	  krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
624 	if (retval) {
625 	  return (retval);
626 	}
627 	if (creds->server)
628 	    krb5_free_principal( context, creds->server);
629 	if (creds->client)
630 	    krb5_free_principal( context, creds->client);
631 	creds->client = client_princ;
632 	creds->server = server_princ;
633 	/* store it in the ccache! */
634 	if (ccache)
635 	  if ((retval = krb5_cc_store_cred(context, ccache, creds)))
636 	    return (retval);
637 	return retval;
638   }
639 
640