1/*
2 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
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.
16 *
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.
20 *
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
31 * SUCH DAMAGE.
32 */
33
34#include "ktutil_locl.h"
35
36RCSID("$Id$");
37
38static krb5_error_code
39change_entry (krb5_keytab keytab,
40	      krb5_principal principal, krb5_kvno kvno,
41	      const char *realm, const char *admin_server, int server_port)
42{
43    krb5_error_code ret;
44    kadm5_config_params conf;
45    void *kadm_handle;
46    char *client_name;
47    krb5_keyblock *keys;
48    int num_keys;
49    int i;
50
51    ret = krb5_unparse_name (context, principal, &client_name);
52    if (ret) {
53	krb5_warn (context, ret, "krb5_unparse_name");
54	return ret;
55    }
56
57    memset (&conf, 0, sizeof(conf));
58
59    if(realm == NULL)
60	realm = krb5_principal_get_realm(context, principal);
61    conf.realm = strdup(realm);
62    if (conf.realm == NULL) {
63	free (client_name);
64	krb5_set_error_message(context, ENOMEM, "malloc failed");
65	return ENOMEM;
66    }
67    conf.mask |= KADM5_CONFIG_REALM;
68
69    if (admin_server) {
70	conf.admin_server = strdup(admin_server);
71	if (conf.admin_server == NULL) {
72	    free(client_name);
73	    free(conf.realm);
74	    krb5_set_error_message(context, ENOMEM, "malloc failed");
75	    return ENOMEM;
76	}
77	conf.mask |= KADM5_CONFIG_ADMIN_SERVER;
78    }
79
80    if (server_port) {
81	conf.kadmind_port = htons(server_port);
82	conf.mask |= KADM5_CONFIG_KADMIND_PORT;
83    }
84
85    ret = kadm5_init_with_skey_ctx (context,
86				    client_name,
87				    keytab_string,
88				    KADM5_ADMIN_SERVICE,
89				    &conf, 0, 0,
90				    &kadm_handle);
91    free(conf.admin_server);
92    free(conf.realm);
93    if (ret) {
94	krb5_warn (context, ret,
95		   "kadm5_c_init_with_skey_ctx: %s:", client_name);
96	free (client_name);
97	return ret;
98    }
99    ret = kadm5_randkey_principal (kadm_handle, principal, &keys, &num_keys);
100    kadm5_destroy (kadm_handle);
101    if (ret) {
102	krb5_warn(context, ret, "kadm5_randkey_principal: %s:", client_name);
103	free (client_name);
104	return ret;
105    }
106    free (client_name);
107    for (i = 0; i < num_keys; ++i) {
108	krb5_keytab_entry new_entry;
109
110	new_entry.principal = principal;
111	new_entry.timestamp = time (NULL);
112	new_entry.vno = kvno + 1;
113	new_entry.keyblock  = keys[i];
114
115	ret = krb5_kt_add_entry (context, keytab, &new_entry);
116	if (ret)
117	    krb5_warn (context, ret, "krb5_kt_add_entry");
118	krb5_free_keyblock_contents (context, &keys[i]);
119    }
120    return ret;
121}
122
123/*
124 * loop over all the entries in the keytab (or those given) and change
125 * their keys, writing the new keys
126 */
127
128struct change_set {
129    krb5_principal principal;
130    krb5_kvno kvno;
131};
132
133int
134kt_change (struct change_options *opt, int argc, char **argv)
135{
136    krb5_error_code ret;
137    krb5_keytab keytab;
138    krb5_kt_cursor cursor;
139    krb5_keytab_entry entry;
140    int i, j, max;
141    struct change_set *changeset;
142    int errors = 0;
143
144    if((keytab = ktutil_open_keytab()) == NULL)
145	return 1;
146
147    j = 0;
148    max = 0;
149    changeset = NULL;
150
151    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
152    if(ret){
153	krb5_warn(context, ret, "%s", keytab_string);
154	goto out;
155    }
156
157    while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
158	int add = 0;
159
160	for (i = 0; i < j; ++i) {
161	    if (krb5_principal_compare (context, changeset[i].principal,
162					entry.principal)) {
163		if (changeset[i].kvno < entry.vno)
164		    changeset[i].kvno = entry.vno;
165		break;
166	    }
167	}
168	if (i < j) {
169	    krb5_kt_free_entry (context, &entry);
170	    continue;
171	}
172
173	if (argc == 0) {
174	    add = 1;
175	} else {
176	    for (i = 0; i < argc; ++i) {
177		krb5_principal princ;
178
179		ret = krb5_parse_name (context, argv[i], &princ);
180		if (ret) {
181		    krb5_warn (context, ret, "%s", argv[i]);
182		    continue;
183		}
184		if (krb5_principal_compare (context, princ, entry.principal))
185		    add = 1;
186
187		krb5_free_principal (context, princ);
188	    }
189	}
190
191	if (add) {
192	    if (j >= max) {
193		void *tmp;
194
195		max = max(max * 2, 1);
196		tmp = realloc (changeset, max * sizeof(*changeset));
197		if (tmp == NULL) {
198		    krb5_kt_free_entry (context, &entry);
199		    krb5_warnx (context, "realloc: out of memory");
200		    ret = ENOMEM;
201		    break;
202		}
203		changeset = tmp;
204	    }
205	    ret = krb5_copy_principal (context, entry.principal,
206				       &changeset[j].principal);
207	    if (ret) {
208		krb5_warn (context, ret, "krb5_copy_principal");
209		krb5_kt_free_entry (context, &entry);
210		break;
211	    }
212	    changeset[j].kvno = entry.vno;
213	    ++j;
214	}
215	krb5_kt_free_entry (context, &entry);
216    }
217    krb5_kt_end_seq_get(context, keytab, &cursor);
218
219    if (ret == KRB5_KT_END) {
220	ret = 0;
221	for (i = 0; i < j; i++) {
222	    if (verbose_flag) {
223		char *client_name;
224
225		ret = krb5_unparse_name (context, changeset[i].principal,
226					 &client_name);
227		if (ret) {
228		    krb5_warn (context, ret, "krb5_unparse_name");
229		} else {
230		    printf("Changing %s kvno %d\n",
231			   client_name, changeset[i].kvno);
232		    free(client_name);
233		}
234	    }
235	    ret = change_entry (keytab,
236				changeset[i].principal, changeset[i].kvno,
237				opt->realm_string,
238				opt->admin_server_string,
239				opt->server_port_integer);
240	    if (ret != 0)
241		errors = 1;
242	}
243    } else
244	errors = 1;
245    for (i = 0; i < j; i++)
246	krb5_free_principal (context, changeset[i].principal);
247    free (changeset);
248
249 out:
250    krb5_kt_close(context, keytab);
251    return errors;
252}
253