1178825Sdfr/*
2178825Sdfr * Copyright (c) 2005, PADL Software Pty Ltd.
3178825Sdfr * All rights reserved.
4178825Sdfr *
5178825Sdfr * Redistribution and use in source and binary forms, with or without
6178825Sdfr * modification, are permitted provided that the following conditions
7178825Sdfr * are met:
8178825Sdfr *
9178825Sdfr * 1. Redistributions of source code must retain the above copyright
10178825Sdfr *    notice, this list of conditions and the following disclaimer.
11178825Sdfr *
12178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
13178825Sdfr *    notice, this list of conditions and the following disclaimer in the
14178825Sdfr *    documentation and/or other materials provided with the distribution.
15178825Sdfr *
16178825Sdfr * 3. Neither the name of PADL Software nor the names of its contributors
17178825Sdfr *    may be used to endorse or promote products derived from this software
18178825Sdfr *    without specific prior written permission.
19178825Sdfr *
20178825Sdfr * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30178825Sdfr * SUCH DAMAGE.
31178825Sdfr */
32178825Sdfr
33178825Sdfr#include "kcm_locl.h"
34178825Sdfr
35178825SdfrRCSID("$Id: acquire.c 22118 2007-12-03 21:44:00Z lha $");
36178825Sdfr
37178825Sdfrstatic krb5_error_code
38178825Sdfrchange_pw_and_update_keytab(krb5_context context, kcm_ccache ccache);
39178825Sdfr
40178825Sdfr/*
41178825Sdfr * Get a new ticket using a keytab/cached key and swap it into
42178825Sdfr * an existing redentials cache
43178825Sdfr */
44178825Sdfr
45178825Sdfrkrb5_error_code
46178825Sdfrkcm_ccache_acquire(krb5_context context,
47178825Sdfr		   kcm_ccache ccache,
48178825Sdfr		   krb5_creds **credp)
49178825Sdfr{
50178825Sdfr    krb5_error_code ret = 0;
51178825Sdfr    krb5_creds cred;
52178825Sdfr    krb5_const_realm realm;
53178825Sdfr    krb5_get_init_creds_opt opt;
54178825Sdfr    krb5_ccache_data ccdata;
55178825Sdfr    char *in_tkt_service = NULL;
56178825Sdfr    int done = 0;
57178825Sdfr
58178825Sdfr    memset(&cred, 0, sizeof(cred));
59178825Sdfr
60178825Sdfr    KCM_ASSERT_VALID(ccache);
61178825Sdfr
62178825Sdfr    /* We need a cached key or keytab to acquire credentials */
63178825Sdfr    if (ccache->flags & KCM_FLAGS_USE_CACHED_KEY) {
64178825Sdfr	if (ccache->key.keyblock.keyvalue.length == 0)
65178825Sdfr	    krb5_abortx(context,
66178825Sdfr			"kcm_ccache_acquire: KCM_FLAGS_USE_CACHED_KEY without key");
67178825Sdfr    } else if (ccache->flags & KCM_FLAGS_USE_KEYTAB) {
68178825Sdfr	if (ccache->key.keytab == NULL)
69178825Sdfr	    krb5_abortx(context,
70178825Sdfr			"kcm_ccache_acquire: KCM_FLAGS_USE_KEYTAB without keytab");
71178825Sdfr    } else {
72178825Sdfr	kcm_log(0, "Cannot acquire initial credentials for cache %s without key",
73178825Sdfr		ccache->name);
74178825Sdfr	return KRB5_FCC_INTERNAL;
75178825Sdfr    }
76178825Sdfr
77178825Sdfr    HEIMDAL_MUTEX_lock(&ccache->mutex);
78178825Sdfr
79178825Sdfr    /* Fake up an internal ccache */
80178825Sdfr    kcm_internal_ccache(context, ccache, &ccdata);
81178825Sdfr
82178825Sdfr    /* Now, actually acquire the creds */
83178825Sdfr    if (ccache->server != NULL) {
84178825Sdfr	ret = krb5_unparse_name(context, ccache->server, &in_tkt_service);
85178825Sdfr	if (ret) {
86178825Sdfr	    kcm_log(0, "Failed to unparse service principal name for cache %s: %s",
87178825Sdfr		    ccache->name, krb5_get_err_text(context, ret));
88178825Sdfr	    return ret;
89178825Sdfr	}
90178825Sdfr    }
91178825Sdfr
92178825Sdfr    realm = krb5_principal_get_realm(context, ccache->client);
93178825Sdfr
94178825Sdfr    krb5_get_init_creds_opt_init(&opt);
95178825Sdfr    krb5_get_init_creds_opt_set_default_flags(context, "kcm", realm, &opt);
96178825Sdfr    if (ccache->tkt_life != 0)
97178825Sdfr	krb5_get_init_creds_opt_set_tkt_life(&opt, ccache->tkt_life);
98178825Sdfr    if (ccache->renew_life != 0)
99178825Sdfr	krb5_get_init_creds_opt_set_renew_life(&opt, ccache->renew_life);
100178825Sdfr
101178825Sdfr    if (ccache->flags & KCM_FLAGS_USE_CACHED_KEY) {
102178825Sdfr	ret = krb5_get_init_creds_keyblock(context,
103178825Sdfr					   &cred,
104178825Sdfr					   ccache->client,
105178825Sdfr					   &ccache->key.keyblock,
106178825Sdfr					   0,
107178825Sdfr					   in_tkt_service,
108178825Sdfr					   &opt);
109178825Sdfr    } else {
110178825Sdfr	/* loosely based on lib/krb5/init_creds_pw.c */
111178825Sdfr	while (!done) {
112178825Sdfr	    ret = krb5_get_init_creds_keytab(context,
113178825Sdfr					     &cred,
114178825Sdfr					     ccache->client,
115178825Sdfr					     ccache->key.keytab,
116178825Sdfr					     0,
117178825Sdfr					     in_tkt_service,
118178825Sdfr					     &opt);
119178825Sdfr	    switch (ret) {
120178825Sdfr	    case KRB5KDC_ERR_KEY_EXPIRED:
121178825Sdfr		if (in_tkt_service != NULL &&
122178825Sdfr		    strcmp(in_tkt_service, "kadmin/changepw") == 0) {
123178825Sdfr		    goto out;
124178825Sdfr		}
125178825Sdfr
126178825Sdfr		ret = change_pw_and_update_keytab(context, ccache);
127178825Sdfr		if (ret)
128178825Sdfr		    goto out;
129178825Sdfr		break;
130178825Sdfr	    case 0:
131178825Sdfr	    default:
132178825Sdfr		done = 1;
133178825Sdfr		break;
134178825Sdfr	    }
135178825Sdfr	}
136178825Sdfr    }
137178825Sdfr
138178825Sdfr    if (ret) {
139178825Sdfr	kcm_log(0, "Failed to acquire credentials for cache %s: %s",
140178825Sdfr		ccache->name, krb5_get_err_text(context, ret));
141178825Sdfr	if (in_tkt_service != NULL)
142178825Sdfr	    free(in_tkt_service);
143178825Sdfr	goto out;
144178825Sdfr    }
145178825Sdfr
146178825Sdfr    if (in_tkt_service != NULL)
147178825Sdfr	free(in_tkt_service);
148178825Sdfr
149178825Sdfr    /* Swap them in */
150178825Sdfr    kcm_ccache_remove_creds_internal(context, ccache);
151178825Sdfr
152178825Sdfr    ret = kcm_ccache_store_cred_internal(context, ccache, &cred, 0, credp);
153178825Sdfr    if (ret) {
154178825Sdfr	kcm_log(0, "Failed to store credentials for cache %s: %s",
155178825Sdfr		ccache->name, krb5_get_err_text(context, ret));
156178825Sdfr	krb5_free_cred_contents(context, &cred);
157178825Sdfr	goto out;
158178825Sdfr    }
159178825Sdfr
160178825Sdfrout:
161178825Sdfr    HEIMDAL_MUTEX_unlock(&ccache->mutex);
162178825Sdfr
163178825Sdfr    return ret;
164178825Sdfr}
165178825Sdfr
166178825Sdfrstatic krb5_error_code
167178825Sdfrchange_pw(krb5_context context,
168178825Sdfr	  kcm_ccache ccache,
169178825Sdfr	  char *cpn,
170178825Sdfr	  char *newpw)
171178825Sdfr{
172178825Sdfr    krb5_error_code ret;
173178825Sdfr    krb5_creds cpw_cred;
174178825Sdfr    int result_code;
175178825Sdfr    krb5_data result_code_string;
176178825Sdfr    krb5_data result_string;
177178825Sdfr    krb5_get_init_creds_opt options;
178178825Sdfr
179178825Sdfr    memset(&cpw_cred, 0, sizeof(cpw_cred));
180178825Sdfr
181178825Sdfr    krb5_get_init_creds_opt_init(&options);
182178825Sdfr    krb5_get_init_creds_opt_set_tkt_life(&options, 60);
183178825Sdfr    krb5_get_init_creds_opt_set_forwardable(&options, FALSE);
184178825Sdfr    krb5_get_init_creds_opt_set_proxiable(&options, FALSE);
185178825Sdfr
186178825Sdfr    krb5_data_zero(&result_code_string);
187178825Sdfr    krb5_data_zero(&result_string);
188178825Sdfr
189178825Sdfr    ret = krb5_get_init_creds_keytab(context,
190178825Sdfr				     &cpw_cred,
191178825Sdfr				     ccache->client,
192178825Sdfr				     ccache->key.keytab,
193178825Sdfr				     0,
194178825Sdfr				     "kadmin/changepw",
195178825Sdfr				     &options);
196178825Sdfr    if (ret) {
197178825Sdfr	kcm_log(0, "Failed to acquire password change credentials "
198178825Sdfr		"for principal %s: %s",
199178825Sdfr		cpn, krb5_get_err_text(context, ret));
200178825Sdfr	goto out;
201178825Sdfr    }
202178825Sdfr
203178825Sdfr    ret = krb5_set_password(context,
204178825Sdfr			    &cpw_cred,
205178825Sdfr			    newpw,
206178825Sdfr			    ccache->client,
207178825Sdfr			    &result_code,
208178825Sdfr			    &result_code_string,
209178825Sdfr			    &result_string);
210178825Sdfr    if (ret) {
211178825Sdfr	kcm_log(0, "Failed to change password for principal %s: %s",
212178825Sdfr		cpn, krb5_get_err_text(context, ret));
213178825Sdfr	goto out;
214178825Sdfr    }
215178825Sdfr
216178825Sdfr    if (result_code) {
217178825Sdfr	kcm_log(0, "Failed to change password for principal %s: %.*s",
218178825Sdfr		cpn,
219178825Sdfr		(int)result_string.length,
220178825Sdfr		result_string.length > 0 ? (char *)result_string.data : "");
221178825Sdfr	goto out;
222178825Sdfr    }
223178825Sdfr
224178825Sdfrout:
225178825Sdfr    krb5_data_free(&result_string);
226178825Sdfr    krb5_data_free(&result_code_string);
227178825Sdfr    krb5_free_cred_contents(context, &cpw_cred);
228178825Sdfr
229178825Sdfr    return ret;
230178825Sdfr}
231178825Sdfr
232178825Sdfrstruct kcm_keyseed_data {
233178825Sdfr    krb5_salt salt;
234178825Sdfr    const char *password;
235178825Sdfr};
236178825Sdfr
237178825Sdfrstatic krb5_error_code
238178825Sdfrkcm_password_key_proc(krb5_context context,
239178825Sdfr		      krb5_enctype etype,
240178825Sdfr		      krb5_salt salt,
241178825Sdfr		      krb5_const_pointer keyseed,
242178825Sdfr		      krb5_keyblock **key)
243178825Sdfr{
244178825Sdfr    krb5_error_code ret;
245178825Sdfr    struct kcm_keyseed_data *s = (struct kcm_keyseed_data *)keyseed;
246178825Sdfr
247178825Sdfr    /* we may be called multiple times */
248178825Sdfr    krb5_free_salt(context, s->salt);
249178825Sdfr    krb5_data_zero(&s->salt.saltvalue);
250178825Sdfr
251178825Sdfr    /* stash the salt */
252178825Sdfr    s->salt.salttype = salt.salttype;
253178825Sdfr
254178825Sdfr    ret = krb5_data_copy(&s->salt.saltvalue,
255178825Sdfr		         salt.saltvalue.data,
256178825Sdfr			 salt.saltvalue.length);
257178825Sdfr    if (ret)
258178825Sdfr	return ret;
259178825Sdfr
260178825Sdfr    *key = (krb5_keyblock *)malloc(sizeof(**key));
261178825Sdfr    if (*key == NULL) {
262178825Sdfr	return ENOMEM;
263178825Sdfr    }
264178825Sdfr
265178825Sdfr    ret = krb5_string_to_key_salt(context, etype, s->password,
266178825Sdfr				  s->salt, *key);
267178825Sdfr    if (ret) {
268178825Sdfr	free(*key);
269178825Sdfr	*key = NULL;
270178825Sdfr    }
271178825Sdfr
272178825Sdfr    return ret;
273178825Sdfr}
274178825Sdfr
275178825Sdfrstatic krb5_error_code
276178825Sdfrget_salt_and_kvno(krb5_context context,
277178825Sdfr		  kcm_ccache ccache,
278178825Sdfr		  krb5_enctype *etypes,
279178825Sdfr		  char *cpn,
280178825Sdfr		  char *newpw,
281178825Sdfr		  krb5_salt *salt,
282178825Sdfr		  unsigned *kvno)
283178825Sdfr{
284178825Sdfr    krb5_error_code ret;
285178825Sdfr    krb5_creds creds;
286178825Sdfr    krb5_ccache_data ccdata;
287178825Sdfr    krb5_flags options = 0;
288178825Sdfr    krb5_kdc_rep reply;
289178825Sdfr    struct kcm_keyseed_data s;
290178825Sdfr
291178825Sdfr    memset(&creds, 0, sizeof(creds));
292178825Sdfr    memset(&reply, 0, sizeof(reply));
293178825Sdfr
294178825Sdfr    s.password = NULL;
295178825Sdfr    s.salt.salttype = (int)ETYPE_NULL;
296178825Sdfr    krb5_data_zero(&s.salt.saltvalue);
297178825Sdfr
298178825Sdfr    *kvno = 0;
299178825Sdfr    kcm_internal_ccache(context, ccache, &ccdata);
300178825Sdfr    s.password = newpw;
301178825Sdfr
302178825Sdfr    /* Do an AS-REQ to determine salt and key version number */
303178825Sdfr    ret = krb5_copy_principal(context, ccache->client, &creds.client);
304178825Sdfr    if (ret)
305178825Sdfr	return ret;
306178825Sdfr
307178825Sdfr    /* Yes, get a ticket to ourselves */
308178825Sdfr    ret = krb5_copy_principal(context, ccache->client, &creds.server);
309178825Sdfr    if (ret) {
310178825Sdfr	krb5_free_principal(context, creds.client);
311178825Sdfr	return ret;
312178825Sdfr    }
313178825Sdfr
314178825Sdfr    ret = krb5_get_in_tkt(context,
315178825Sdfr			  options,
316178825Sdfr			  NULL,
317178825Sdfr			  etypes,
318178825Sdfr			  NULL,
319178825Sdfr			  kcm_password_key_proc,
320178825Sdfr			  &s,
321178825Sdfr			  NULL,
322178825Sdfr			  NULL,
323178825Sdfr			  &creds,
324178825Sdfr			  &ccdata,
325178825Sdfr			  &reply);
326178825Sdfr    if (ret) {
327178825Sdfr	kcm_log(0, "Failed to get self ticket for principal %s: %s",
328178825Sdfr		cpn, krb5_get_err_text(context, ret));
329178825Sdfr	krb5_free_salt(context, s.salt);
330178825Sdfr    } else {
331178825Sdfr	*salt = s.salt; /* retrieve stashed salt */
332178825Sdfr	if (reply.kdc_rep.enc_part.kvno != NULL)
333178825Sdfr	    *kvno = *(reply.kdc_rep.enc_part.kvno);
334178825Sdfr    }
335178825Sdfr    /* ccache may have been modified but it will get trashed anyway */
336178825Sdfr
337178825Sdfr    krb5_free_cred_contents(context, &creds);
338178825Sdfr    krb5_free_kdc_rep(context, &reply);
339178825Sdfr
340178825Sdfr    return ret;
341178825Sdfr}
342178825Sdfr
343178825Sdfrstatic krb5_error_code
344178825Sdfrupdate_keytab_entry(krb5_context context,
345178825Sdfr		    kcm_ccache ccache,
346178825Sdfr		    krb5_enctype etype,
347178825Sdfr		    char *cpn,
348178825Sdfr		    char *spn,
349178825Sdfr		    char *newpw,
350178825Sdfr		    krb5_salt salt,
351178825Sdfr		    unsigned kvno)
352178825Sdfr{
353178825Sdfr    krb5_error_code ret;
354178825Sdfr    krb5_keytab_entry entry;
355178825Sdfr    krb5_data pw;
356178825Sdfr
357178825Sdfr    memset(&entry, 0, sizeof(entry));
358178825Sdfr
359178825Sdfr    pw.data = (char *)newpw;
360178825Sdfr    pw.length = strlen(newpw);
361178825Sdfr
362178825Sdfr    ret = krb5_string_to_key_data_salt(context, etype, pw,
363178825Sdfr				       salt, &entry.keyblock);
364178825Sdfr    if (ret) {
365178825Sdfr	kcm_log(0, "String to key conversion failed for principal %s "
366178825Sdfr		"and etype %d: %s",
367178825Sdfr		cpn, etype, krb5_get_err_text(context, ret));
368178825Sdfr	return ret;
369178825Sdfr    }
370178825Sdfr
371178825Sdfr    if (spn == NULL) {
372178825Sdfr	ret = krb5_copy_principal(context, ccache->client,
373178825Sdfr				  &entry.principal);
374178825Sdfr	if (ret) {
375178825Sdfr	    kcm_log(0, "Failed to copy principal name %s: %s",
376178825Sdfr		    cpn, krb5_get_err_text(context, ret));
377178825Sdfr	    return ret;
378178825Sdfr	}
379178825Sdfr    } else {
380178825Sdfr	ret = krb5_parse_name(context, spn, &entry.principal);
381178825Sdfr	if (ret) {
382178825Sdfr	    kcm_log(0, "Failed to parse SPN alias %s: %s",
383178825Sdfr		    spn, krb5_get_err_text(context, ret));
384178825Sdfr	    return ret;
385178825Sdfr	}
386178825Sdfr    }
387178825Sdfr
388178825Sdfr    entry.vno = kvno;
389178825Sdfr    entry.timestamp = time(NULL);
390178825Sdfr
391178825Sdfr    ret = krb5_kt_add_entry(context, ccache->key.keytab, &entry);
392178825Sdfr    if (ret) {
393178825Sdfr	kcm_log(0, "Failed to update keytab for principal %s "
394178825Sdfr		"and etype %d: %s",
395178825Sdfr		cpn, etype, krb5_get_err_text(context, ret));
396178825Sdfr    }
397178825Sdfr
398178825Sdfr    krb5_kt_free_entry(context, &entry);
399178825Sdfr
400178825Sdfr    return ret;
401178825Sdfr}
402178825Sdfr
403178825Sdfrstatic krb5_error_code
404178825Sdfrupdate_keytab_entries(krb5_context context,
405178825Sdfr		      kcm_ccache ccache,
406178825Sdfr		      krb5_enctype *etypes,
407178825Sdfr		      char *cpn,
408178825Sdfr		      char *spn,
409178825Sdfr		      char *newpw,
410178825Sdfr		      krb5_salt salt,
411178825Sdfr		      unsigned kvno)
412178825Sdfr{
413178825Sdfr    krb5_error_code ret = 0;
414178825Sdfr    int i;
415178825Sdfr
416178825Sdfr    for (i = 0; etypes[i] != ETYPE_NULL; i++) {
417178825Sdfr	ret = update_keytab_entry(context, ccache, etypes[i],
418178825Sdfr				  cpn, spn, newpw, salt, kvno);
419178825Sdfr	if (ret)
420178825Sdfr	    break;
421178825Sdfr    }
422178825Sdfr
423178825Sdfr    return ret;
424178825Sdfr}
425178825Sdfr
426178825Sdfrstatic void
427178825Sdfrgenerate_random_pw(krb5_context context,
428178825Sdfr		   char *buf,
429178825Sdfr		   size_t bufsiz)
430178825Sdfr{
431178825Sdfr    unsigned char x[512], *p;
432178825Sdfr    size_t i;
433178825Sdfr
434178825Sdfr    memset(x, 0, sizeof(x));
435178825Sdfr    krb5_generate_random_block(x, sizeof(x));
436178825Sdfr    p = x;
437178825Sdfr
438178825Sdfr    for (i = 0; i < bufsiz; i++) {
439178825Sdfr	while (isprint(*p) == 0)
440178825Sdfr	    p++;
441178825Sdfr
442178825Sdfr	if (p - x >= sizeof(x)) {
443178825Sdfr	    krb5_generate_random_block(x, sizeof(x));
444178825Sdfr	    p = x;
445178825Sdfr	}
446178825Sdfr	buf[i] = (char)*p++;
447178825Sdfr    }
448178825Sdfr    buf[bufsiz - 1] = '\0';
449178825Sdfr    memset(x, 0, sizeof(x));
450178825Sdfr}
451178825Sdfr
452178825Sdfrstatic krb5_error_code
453178825Sdfrchange_pw_and_update_keytab(krb5_context context,
454178825Sdfr			    kcm_ccache ccache)
455178825Sdfr{
456178825Sdfr    char newpw[121];
457178825Sdfr    krb5_error_code ret;
458178825Sdfr    unsigned kvno;
459178825Sdfr    krb5_salt salt;
460178825Sdfr    krb5_enctype *etypes = NULL;
461178825Sdfr    int i;
462178825Sdfr    char *cpn = NULL;
463178825Sdfr    char **spns = NULL;
464178825Sdfr
465178825Sdfr    krb5_data_zero(&salt.saltvalue);
466178825Sdfr
467178825Sdfr    ret = krb5_unparse_name(context, ccache->client, &cpn);
468178825Sdfr    if (ret) {
469178825Sdfr	kcm_log(0, "Failed to unparse name: %s",
470178825Sdfr		krb5_get_err_text(context, ret));
471178825Sdfr	goto out;
472178825Sdfr    }
473178825Sdfr
474178825Sdfr    ret = krb5_get_default_in_tkt_etypes(context, &etypes);
475178825Sdfr    if (ret) {
476178825Sdfr	kcm_log(0, "Failed to determine default encryption types: %s",
477178825Sdfr		krb5_get_err_text(context, ret));
478178825Sdfr	goto out;
479178825Sdfr    }
480178825Sdfr
481178825Sdfr    /* Generate a random password (there is no set keys protocol) */
482178825Sdfr    generate_random_pw(context, newpw, sizeof(newpw));
483178825Sdfr
484178825Sdfr    /* Change it */
485178825Sdfr    ret = change_pw(context, ccache, cpn, newpw);
486178825Sdfr    if (ret)
487178825Sdfr	goto out;
488178825Sdfr
489178825Sdfr    /* Do an AS-REQ to determine salt and key version number */
490178825Sdfr    ret = get_salt_and_kvno(context, ccache, etypes, cpn, newpw,
491178825Sdfr			    &salt, &kvno);
492178825Sdfr    if (ret) {
493178825Sdfr	kcm_log(0, "Failed to determine salting principal for principal %s: %s",
494178825Sdfr		cpn, krb5_get_err_text(context, ret));
495178825Sdfr	goto out;
496178825Sdfr    }
497178825Sdfr
498178825Sdfr    /* Add canonical name */
499178825Sdfr    ret = update_keytab_entries(context, ccache, etypes, cpn,
500178825Sdfr				NULL, newpw, salt, kvno);
501178825Sdfr    if (ret)
502178825Sdfr	goto out;
503178825Sdfr
504178825Sdfr    /* Add SPN aliases, if any */
505178825Sdfr    spns = krb5_config_get_strings(context, NULL, "kcm",
506178825Sdfr				   "system_ccache", "spn_aliases", NULL);
507178825Sdfr    if (spns != NULL) {
508178825Sdfr	for (i = 0; spns[i] != NULL; i++) {
509178825Sdfr	    ret = update_keytab_entries(context, ccache, etypes, cpn,
510178825Sdfr					spns[i], newpw, salt, kvno);
511178825Sdfr	    if (ret)
512178825Sdfr		goto out;
513178825Sdfr	}
514178825Sdfr    }
515178825Sdfr
516178825Sdfr    kcm_log(0, "Changed expired password for principal %s in cache %s",
517178825Sdfr	    cpn, ccache->name);
518178825Sdfr
519178825Sdfrout:
520178825Sdfr    if (cpn != NULL)
521178825Sdfr	free(cpn);
522178825Sdfr    if (spns != NULL)
523178825Sdfr	krb5_config_free_strings(spns);
524178825Sdfr    if (etypes != NULL)
525178825Sdfr	free(etypes);
526178825Sdfr    krb5_free_salt(context, salt);
527178825Sdfr    memset(newpw, 0, sizeof(newpw));
528178825Sdfr
529178825Sdfr    return ret;
530178825Sdfr}
531178825Sdfr
532