s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / source4 / heimdal / admin / change.c
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
36 RCSID("$Id$");
37
38 static krb5_error_code
39 change_entry(krb5_keytab keytab,
40              krb5_principal principal,
41              krb5_kvno kvno,
42              int keep,
43              size_t nkstuple,
44              krb5_key_salt_tuple *kstuple,
45              const char *realm,
46              const char *admin_server,
47              int server_port)
48 {
49     krb5_error_code ret;
50     kadm5_config_params conf;
51     void *kadm_handle;
52     char *client_name;
53     krb5_keyblock *keys;
54     size_t i;
55     int num_keys;
56
57     ret = krb5_unparse_name (context, principal, &client_name);
58     if (ret) {
59         krb5_warn (context, ret, "krb5_unparse_name");
60         return ret;
61     }
62
63     memset (&conf, 0, sizeof(conf));
64
65     if(realm == NULL)
66         realm = krb5_principal_get_realm(context, principal);
67     conf.realm = strdup(realm);
68     if (conf.realm == NULL) {
69         free (client_name);
70         krb5_set_error_message(context, ENOMEM, "malloc failed");
71         return ENOMEM;
72     }
73     conf.mask |= KADM5_CONFIG_REALM;
74
75     if (admin_server) {
76         conf.admin_server = strdup(admin_server);
77         if (conf.admin_server == NULL) {
78             free(client_name);
79             free(conf.realm);
80             krb5_set_error_message(context, ENOMEM, "malloc failed");
81             return ENOMEM;
82         }
83         conf.mask |= KADM5_CONFIG_ADMIN_SERVER;
84     }
85
86     if (server_port) {
87         conf.kadmind_port = htons(server_port);
88         conf.mask |= KADM5_CONFIG_KADMIND_PORT;
89     }
90
91     ret = kadm5_init_with_skey_ctx (context,
92                                     client_name,
93                                     keytab_string,
94                                     KADM5_ADMIN_SERVICE,
95                                     &conf, 0, 0,
96                                     &kadm_handle);
97     free(conf.admin_server);
98     free(conf.realm);
99     if (ret) {
100         krb5_warn (context, ret,
101                    "kadm5_c_init_with_skey_ctx: %s:", client_name);
102         free (client_name);
103         return ret;
104     }
105     ret = kadm5_randkey_principal_3(kadm_handle, principal, keep, nkstuple,
106                                     kstuple, &keys, &num_keys);
107     kadm5_destroy(kadm_handle);
108     if (ret) {
109         krb5_warn(context, ret, "kadm5_randkey_principal_3: %s:", client_name);
110         free (client_name);
111         return ret;
112     }
113     free(client_name);
114     for (i = 0; i < num_keys; ++i) {
115         krb5_keytab_entry new_entry;
116
117         new_entry.principal = principal;
118         new_entry.timestamp = time (NULL);
119         new_entry.vno = kvno + 1;
120         new_entry.keyblock  = keys[i];
121
122         ret = krb5_kt_add_entry (context, keytab, &new_entry);
123         if (ret)
124             krb5_warn (context, ret, "krb5_kt_add_entry");
125         krb5_free_keyblock_contents (context, &keys[i]);
126     }
127     return ret;
128 }
129
130 /*
131  * loop over all the entries in the keytab (or those given) and change
132  * their keys, writing the new keys
133  */
134
135 struct change_set {
136     krb5_principal principal;
137     krb5_kvno kvno;
138 };
139
140 int
141 kt_change(struct change_options *opt, int argc, char **argv)
142 {
143     krb5_error_code ret;
144     krb5_keytab keytab;
145     krb5_kt_cursor cursor;
146     krb5_keytab_entry entry;
147     krb5_key_salt_tuple *kstuple = NULL;
148     const char *enctype;
149     size_t i, j, max, nkstuple;
150     int keep = 1;
151     struct change_set *changeset;
152     int errors = 0;
153
154     i = 0;
155
156     if (opt->keepold_flag) {
157         keep = 1;
158         i++;
159     }
160     if (opt->keepallold_flag) {
161         keep = 2;
162         i++;
163     }
164     if (opt->pruneall_flag) {
165         keep = 0;
166         i++;
167     }
168     if (i > 1) {
169         fprintf(stderr, "use only one of --keepold, --keepallold, or --pruneall\n");
170         return EINVAL;
171     }
172
173     enctype = opt->enctype_string;
174     if (enctype == NULL || enctype[0] == '\0')
175         enctype = krb5_config_get_string(context, NULL, "libdefaults",
176                                          "supported_enctypes", NULL);
177     if (enctype == NULL || enctype[0] == '\0')
178         enctype = "aes128-cts-hmac-sha1-96";
179     ret = krb5_string_to_keysalts2(context, enctype, &nkstuple, &kstuple);
180     if (ret) {
181         fprintf(stderr, "enctype(s) unknown\n");
182         return ret;
183     }
184
185     /* XXX Parameterize keytab name */
186     if ((keytab = ktutil_open_keytab()) == NULL) {
187         free(kstuple);
188         return 1;
189     }
190
191     j = 0;
192     max = 0;
193     changeset = NULL;
194
195     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
196     if(ret){
197         krb5_warn(context, ret, "%s", keytab_string);
198         goto out;
199     }
200
201     while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
202         int add = 0;
203
204         for (i = 0; i < j; ++i) {
205             if (krb5_principal_compare (context, changeset[i].principal,
206                                         entry.principal)) {
207                 if (changeset[i].kvno < entry.vno)
208                     changeset[i].kvno = entry.vno;
209                 break;
210             }
211         }
212         if (i < j) {
213             krb5_kt_free_entry (context, &entry);
214             continue;
215         }
216
217         if (argc == 0) {
218             add = 1;
219         } else {
220             for (i = 0; i < argc; ++i) {
221                 krb5_principal princ;
222
223                 ret = krb5_parse_name (context, argv[i], &princ);
224                 if (ret) {
225                     krb5_warn (context, ret, "%s", argv[i]);
226                     continue;
227                 }
228                 if (krb5_principal_compare (context, princ, entry.principal))
229                     add = 1;
230
231                 krb5_free_principal (context, princ);
232             }
233         }
234
235         if (add) {
236             if (j >= max) {
237                 void *tmp;
238
239                 max = max(max * 2, 1);
240                 tmp = realloc (changeset, max * sizeof(*changeset));
241                 if (tmp == NULL) {
242                     krb5_kt_free_entry (context, &entry);
243                     krb5_warnx (context, "realloc: out of memory");
244                     ret = ENOMEM;
245                     break;
246                 }
247                 changeset = tmp;
248             }
249             ret = krb5_copy_principal (context, entry.principal,
250                                        &changeset[j].principal);
251             if (ret) {
252                 krb5_warn (context, ret, "krb5_copy_principal");
253                 krb5_kt_free_entry (context, &entry);
254                 break;
255             }
256             changeset[j].kvno = entry.vno;
257             ++j;
258         }
259         krb5_kt_free_entry (context, &entry);
260     }
261     krb5_kt_end_seq_get(context, keytab, &cursor);
262
263     if (ret == KRB5_KT_END) {
264         ret = 0;
265         for (i = 0; i < j; i++) {
266             if (verbose_flag) {
267                 char *client_name;
268
269                 ret = krb5_unparse_name (context, changeset[i].principal,
270                                          &client_name);
271                 if (ret) {
272                     krb5_warn (context, ret, "krb5_unparse_name");
273                 } else {
274                     printf("Changing %s kvno %d\n",
275                            client_name, changeset[i].kvno);
276                     free(client_name);
277                 }
278             }
279             ret = change_entry(keytab,
280                                changeset[i].principal, changeset[i].kvno,
281                                keep, nkstuple, kstuple,
282                                opt->realm_string,
283                                opt->admin_server_string,
284                                opt->server_port_integer);
285             if (ret != 0)
286                 errors = 1;
287         }
288     } else
289         errors = 1;
290     for (i = 0; i < j; i++)
291         krb5_free_principal (context, changeset[i].principal);
292     free (changeset);
293
294  out:
295     free(kstuple);
296     krb5_kt_close(context, keytab);
297     return errors;
298 }