s4-auth: Use kerberos util functions in srv_keytab
[Samba.git] / source4 / auth / kerberos / srv_keytab.c
blob32200850c117c0312018cb07d2f3ed717dd5f814
1 /*
2 Unix SMB/CIFS implementation.
4 Kerberos utility functions
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "auth/credentials/credentials.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/kerberos/kerberos_util.h"
29 #include "auth/kerberos/kerberos_srv_keytab.h"
31 static void keytab_principals_free(krb5_context context,
32 uint32_t num_principals,
33 krb5_principal *set)
35 uint32_t i;
37 for (i = 0; i < num_principals; i++) {
38 krb5_free_principal(context, set[i]);
42 static krb5_error_code salt_principal(TALLOC_CTX *parent_ctx,
43 const char *samAccountName,
44 const char *realm,
45 const char *saltPrincipal,
46 krb5_context context,
47 krb5_principal *salt_princ,
48 const char **error_string)
51 krb5_error_code ret;
52 char *machine_username;
53 char *salt_body;
54 char *lower_realm;
55 char *upper_realm;
57 TALLOC_CTX *tmp_ctx;
59 if (saltPrincipal) {
60 ret = krb5_parse_name(context, saltPrincipal, salt_princ);
61 if (ret) {
62 *error_string = smb_get_krb5_error_message(
63 context, ret, parent_ctx);
65 return ret;
68 if (!samAccountName) {
69 (*error_string) = "Cannot determine salt principal, no "
70 "saltPrincipal or samAccountName specified";
71 return EINVAL;
74 if (!realm) {
75 *error_string = "Cannot make principal without a realm";
76 return EINVAL;
79 tmp_ctx = talloc_new(parent_ctx);
80 if (!tmp_ctx) {
81 *error_string = "Cannot allocate tmp_ctx";
82 return ENOMEM;
85 machine_username = strlower_talloc(tmp_ctx, samAccountName);
86 if (!machine_username) {
87 *error_string = "Cannot duplicate samAccountName";
88 talloc_free(tmp_ctx);
89 return ENOMEM;
92 if (machine_username[strlen(machine_username)-1] == '$') {
93 machine_username[strlen(machine_username)-1] = '\0';
96 lower_realm = strlower_talloc(tmp_ctx, realm);
97 if (!lower_realm) {
98 *error_string = "Cannot allocate to lower case realm";
99 talloc_free(tmp_ctx);
100 return ENOMEM;
103 upper_realm = strupper_talloc(tmp_ctx, realm);
104 if (!upper_realm) {
105 *error_string = "Cannot allocate to upper case realm";
106 talloc_free(tmp_ctx);
107 return ENOMEM;
110 salt_body = talloc_asprintf(tmp_ctx, "%s.%s",
111 machine_username, lower_realm);
112 if (!salt_body) {
113 *error_string = "Cannot form salt principal body";
114 talloc_free(tmp_ctx);
115 return ENOMEM;
118 ret = smb_krb5_make_principal(context, salt_princ, upper_realm,
119 "host", salt_body, NULL);
120 if (ret) {
121 *error_string = smb_get_krb5_error_message(context,
122 ret, parent_ctx);
125 talloc_free(tmp_ctx);
126 return ret;
129 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
130 uint32_t num_principals,
131 krb5_principal *principals,
132 krb5_principal salt_princ,
133 int kvno,
134 const char *password_s,
135 krb5_context context,
136 krb5_enctype *enctypes,
137 krb5_keytab keytab,
138 const char **error_string)
140 unsigned int i, p;
141 krb5_error_code ret;
142 krb5_data password;
143 char *unparsed;
145 password.data = discard_const_p(char, password_s);
146 password.length = strlen(password_s);
148 for (i = 0; enctypes[i]; i++) {
149 krb5_keytab_entry entry;
151 ZERO_STRUCT(entry);
153 ret = smb_krb5_create_key_from_string(context,
154 salt_princ,
155 NULL,
156 &password,
157 enctypes[i],
158 KRB5_KT_KEY(&entry));
159 if (ret != 0) {
160 return ret;
163 entry.vno = kvno;
165 for (p = 0; p < num_principals; p++) {
166 unparsed = NULL;
167 entry.principal = principals[p];
168 ret = krb5_kt_add_entry(context, keytab, &entry);
169 if (ret != 0) {
170 char *k5_error_string =
171 smb_get_krb5_error_message(context,
172 ret, NULL);
173 krb5_unparse_name(context,
174 principals[p], &unparsed);
175 *error_string = talloc_asprintf(parent_ctx,
176 "Failed to add enctype %d entry for "
177 "%s(kvno %d) to keytab: %s\n",
178 (int)enctypes[i], unparsed,
179 kvno, k5_error_string);
181 free(unparsed);
182 talloc_free(k5_error_string);
183 krb5_free_keyblock_contents(context,
184 KRB5_KT_KEY(&entry));
185 return ret;
188 DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
189 kvno, (int)enctypes[i]));
191 krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
193 return 0;
196 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
197 const char *samAccountName,
198 const char *realm,
199 const char *saltPrincipal,
200 int kvno,
201 const char *new_secret,
202 const char *old_secret,
203 uint32_t supp_enctypes,
204 uint32_t num_principals,
205 krb5_principal *principals,
206 krb5_context context,
207 krb5_keytab keytab,
208 bool add_old,
209 const char **error_string)
211 krb5_error_code ret;
212 krb5_principal salt_princ = NULL;
213 krb5_enctype *enctypes;
214 TALLOC_CTX *mem_ctx;
216 if (!new_secret) {
217 /* There is no password here, so nothing to do */
218 return 0;
221 mem_ctx = talloc_new(parent_ctx);
222 if (!mem_ctx) {
223 *error_string = talloc_strdup(parent_ctx,
224 "unable to allocate tmp_ctx for create_keytab");
225 return ENOMEM;
228 /* The salt used to generate these entries may be different however,
229 * fetch that */
230 ret = salt_principal(mem_ctx, samAccountName, realm, saltPrincipal,
231 context, &salt_princ, error_string);
232 if (ret) {
233 talloc_free(mem_ctx);
234 return ret;
237 ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
238 if (ret) {
239 *error_string = talloc_asprintf(parent_ctx,
240 "create_keytab: generating list of "
241 "encryption types failed (%s)\n",
242 smb_get_krb5_error_message(context,
243 ret, mem_ctx));
244 goto done;
247 ret = keytab_add_keys(mem_ctx,
248 num_principals,
249 principals,
250 salt_princ, kvno, new_secret,
251 context, enctypes, keytab, error_string);
252 if (ret) {
253 talloc_steal(parent_ctx, *error_string);
254 goto done;
257 if (old_secret && add_old && kvno != 0) {
258 ret = keytab_add_keys(mem_ctx,
259 num_principals,
260 principals,
261 salt_princ, kvno - 1, old_secret,
262 context, enctypes, keytab, error_string);
263 if (ret) {
264 talloc_steal(parent_ctx, *error_string);
268 done:
269 krb5_free_principal(context, salt_princ);
270 talloc_free(mem_ctx);
271 return ret;
274 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
275 krb5_context context,
276 const char *keytab_name,
277 const char *samAccountName,
278 const char *realm,
279 const char **SPNs,
280 int num_SPNs,
281 const char *saltPrincipal,
282 const char *new_secret,
283 const char *old_secret,
284 int kvno,
285 uint32_t supp_enctypes,
286 bool delete_all_kvno,
287 krb5_keytab *_keytab,
288 const char **error_string)
290 krb5_keytab keytab;
291 krb5_error_code ret;
292 bool found_previous = false;
293 TALLOC_CTX *tmp_ctx;
294 krb5_principal *principals = NULL;
295 uint32_t num_principals = 0;
296 char *upper_realm;
298 if (keytab_name == NULL) {
299 return ENOENT;
302 ret = krb5_kt_resolve(context, keytab_name, &keytab);
303 if (ret) {
304 *error_string = smb_get_krb5_error_message(context,
305 ret, parent_ctx);
306 return ret;
309 DEBUG(5, ("Opened keytab %s\n", keytab_name));
311 tmp_ctx = talloc_new(parent_ctx);
312 if (!tmp_ctx) {
313 return ENOMEM;
316 upper_realm = strupper_talloc(tmp_ctx, realm);
317 if (upper_realm == NULL) {
318 *error_string = "Cannot allocate memory to upper case realm";
319 talloc_free(tmp_ctx);
320 return ENOMEM;
323 ret = smb_krb5_create_principals_array(tmp_ctx,
324 context,
325 samAccountName,
326 upper_realm,
327 num_SPNs,
328 SPNs,
329 &num_principals,
330 &principals,
331 error_string);
332 if (ret != 0) {
333 *error_string = talloc_asprintf(parent_ctx,
334 "Failed to load principals from ldb message: %s\n",
335 *error_string);
336 goto done;
339 ret = smb_krb5_remove_obsolete_keytab_entries(tmp_ctx,
340 context,
341 keytab,
342 num_principals,
343 principals,
344 kvno,
345 &found_previous,
346 error_string);
347 if (ret != 0) {
348 *error_string = talloc_asprintf(parent_ctx,
349 "Failed to remove old principals from keytab: %s\n",
350 *error_string);
351 goto done;
354 if (!delete_all_kvno) {
355 /* Create a new keytab. If during the cleanout we found
356 * entires for kvno -1, then don't try and duplicate them.
357 * Otherwise, add kvno, and kvno -1 */
359 ret = create_keytab(tmp_ctx,
360 samAccountName, upper_realm, saltPrincipal,
361 kvno, new_secret, old_secret,
362 supp_enctypes,
363 num_principals,
364 principals,
365 context, keytab,
366 found_previous ? false : true,
367 error_string);
368 if (ret) {
369 talloc_steal(parent_ctx, *error_string);
373 if (ret == 0 && _keytab != NULL) {
374 /* caller wants the keytab handle back */
375 *_keytab = keytab;
378 done:
379 keytab_principals_free(context, num_principals, principals);
380 if (ret != 0 || _keytab == NULL) {
381 krb5_kt_close(context, keytab);
383 talloc_free(tmp_ctx);
384 return ret;
387 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
388 krb5_context context,
389 const char *new_secret,
390 const char *samAccountName,
391 const char *realm,
392 int kvno,
393 krb5_keytab *keytab,
394 const char **keytab_name)
396 krb5_error_code ret;
397 TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
398 const char *rand_string;
399 const char *error_string;
400 if (!mem_ctx) {
401 return ENOMEM;
404 rand_string = generate_random_str(mem_ctx, 16);
405 if (!rand_string) {
406 talloc_free(mem_ctx);
407 return ENOMEM;
410 *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
411 if (*keytab_name == NULL) {
412 talloc_free(mem_ctx);
413 return ENOMEM;
416 ret = smb_krb5_update_keytab(mem_ctx, context,
417 *keytab_name, samAccountName, realm,
418 NULL, 0, NULL, new_secret, NULL,
419 kvno, ENC_ALL_TYPES,
420 false, keytab, &error_string);
421 if (ret == 0) {
422 talloc_steal(parent_ctx, *keytab_name);
423 } else {
424 DEBUG(0, ("Failed to create in-memory keytab: %s\n",
425 error_string));
426 *keytab_name = NULL;
428 talloc_free(mem_ctx);
429 return ret;