Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Kerberos utility functions
5 :
6 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7 :
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.
12 :
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.
17 :
18 :
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/>.
21 : */
22 :
23 : /**
24 : * @file srv_keytab.c
25 : *
26 : * @brief Kerberos keytab utility functions
27 : *
28 : */
29 :
30 : #include "includes.h"
31 : #include "system/kerberos.h"
32 : #include "auth/credentials/credentials.h"
33 : #include "auth/kerberos/kerberos.h"
34 : #include "auth/kerberos/kerberos_util.h"
35 : #include "auth/kerberos/kerberos_srv_keytab.h"
36 :
37 241 : static void keytab_principals_free(krb5_context context,
38 : uint32_t num_principals,
39 : krb5_principal *set)
40 : {
41 : uint32_t i;
42 :
43 811 : for (i = 0; i < num_principals; i++) {
44 570 : krb5_free_principal(context, set[i]);
45 : }
46 241 : }
47 :
48 238 : static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
49 : uint32_t num_principals,
50 : krb5_principal *principals,
51 : krb5_principal salt_princ,
52 : int kvno,
53 : const char *password_s,
54 : krb5_context context,
55 : krb5_enctype *enctypes,
56 : krb5_keytab keytab,
57 : const char **error_string)
58 : {
59 : unsigned int i, p;
60 : krb5_error_code ret;
61 : krb5_data password;
62 : char *unparsed;
63 :
64 238 : password.data = discard_const_p(char, password_s);
65 238 : password.length = strlen(password_s);
66 :
67 1768 : for (i = 0; enctypes[i]; i++) {
68 : krb5_keytab_entry entry;
69 :
70 714 : ZERO_STRUCT(entry);
71 :
72 714 : ret = smb_krb5_create_key_from_string(context,
73 : salt_princ,
74 : NULL,
75 : &password,
76 714 : enctypes[i],
77 : KRB5_KT_KEY(&entry));
78 714 : if (ret != 0) {
79 0 : *error_string = talloc_strdup(parent_ctx,
80 : "Failed to create key from string");
81 0 : return ret;
82 : }
83 :
84 714 : entry.vno = kvno;
85 :
86 2406 : for (p = 0; p < num_principals; p++) {
87 1692 : unparsed = NULL;
88 1692 : entry.principal = principals[p];
89 1692 : ret = krb5_kt_add_entry(context, keytab, &entry);
90 1692 : if (ret != 0) {
91 0 : char *k5_error_string =
92 0 : smb_get_krb5_error_message(context,
93 : ret, NULL);
94 0 : krb5_unparse_name(context,
95 0 : principals[p], &unparsed);
96 0 : *error_string = talloc_asprintf(parent_ctx,
97 : "Failed to add enctype %d entry for "
98 : "%s(kvno %d) to keytab: %s\n",
99 0 : (int)enctypes[i], unparsed,
100 : kvno, k5_error_string);
101 :
102 0 : free(unparsed);
103 0 : talloc_free(k5_error_string);
104 0 : krb5_free_keyblock_contents(context,
105 : KRB5_KT_KEY(&entry));
106 0 : return ret;
107 : }
108 :
109 1692 : DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
110 : kvno, (int)enctypes[i]));
111 : }
112 714 : krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
113 : }
114 238 : return 0;
115 : }
116 :
117 239 : static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
118 : const char *samAccountName,
119 : const char *realm,
120 : const char *saltPrincipal,
121 : int kvno,
122 : const char *new_secret,
123 : const char *old_secret,
124 : uint32_t supp_enctypes,
125 : uint32_t num_principals,
126 : krb5_principal *principals,
127 : krb5_context context,
128 : krb5_keytab keytab,
129 : bool add_old,
130 : const char **perror_string)
131 : {
132 : krb5_error_code ret;
133 239 : krb5_principal salt_princ = NULL;
134 : krb5_enctype *enctypes;
135 : TALLOC_CTX *mem_ctx;
136 239 : const char *error_string = NULL;
137 :
138 239 : if (!new_secret) {
139 : /* There is no password here, so nothing to do */
140 1 : return 0;
141 : }
142 :
143 238 : mem_ctx = talloc_new(parent_ctx);
144 238 : if (!mem_ctx) {
145 0 : *perror_string = talloc_strdup(parent_ctx,
146 : "unable to allocate tmp_ctx for create_keytab");
147 0 : return ENOMEM;
148 : }
149 :
150 : /* The salt used to generate these entries may be different however,
151 : * fetch that */
152 238 : ret = krb5_parse_name(context, saltPrincipal, &salt_princ);
153 238 : if (ret) {
154 0 : *perror_string = smb_get_krb5_error_message(context,
155 : ret,
156 : parent_ctx);
157 0 : talloc_free(mem_ctx);
158 0 : return ret;
159 : }
160 :
161 238 : ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
162 238 : if (ret) {
163 0 : *perror_string = talloc_asprintf(parent_ctx,
164 : "create_keytab: generating list of "
165 : "encryption types failed (%s)\n",
166 : smb_get_krb5_error_message(context,
167 : ret, mem_ctx));
168 0 : goto done;
169 : }
170 :
171 238 : ret = keytab_add_keys(mem_ctx,
172 : num_principals,
173 : principals,
174 : salt_princ, kvno, new_secret,
175 : context, enctypes, keytab, &error_string);
176 238 : if (ret) {
177 0 : *perror_string = talloc_steal(parent_ctx, error_string);
178 0 : goto done;
179 : }
180 :
181 268 : if (old_secret && add_old && kvno != 0) {
182 0 : ret = keytab_add_keys(mem_ctx,
183 : num_principals,
184 : principals,
185 : salt_princ, kvno - 1, old_secret,
186 : context, enctypes, keytab, &error_string);
187 0 : if (ret) {
188 0 : *perror_string = talloc_steal(parent_ctx, error_string);
189 : }
190 : }
191 :
192 442 : done:
193 238 : krb5_free_principal(context, salt_princ);
194 238 : talloc_free(mem_ctx);
195 238 : return ret;
196 : }
197 :
198 : /**
199 : * @brief Update a Kerberos keytab and removes any obsolete keytab entries.
200 : *
201 : * If the keytab does not exist, this function will create one.
202 : *
203 : * @param[in] parent_ctx Talloc memory context
204 : * @param[in] context Kerberos context
205 : * @param[in] keytab_name Keytab to open
206 : * @param[in] samAccountName User account to update
207 : * @param[in] realm Kerberos realm
208 : * @param[in] SPNs Service principal names to update
209 : * @param[in] num_SPNs Length of SPNs
210 : * @param[in] saltPrincipal Salt used for AES encryption.
211 : * Required, unless delete_all_kvno is set.
212 : * @param[in] old_secret Old password
213 : * @param[in] new_secret New password
214 : * @param[in] kvno Current key version number
215 : * @param[in] supp_enctypes msDS-SupportedEncryptionTypes bit-field
216 : * @param[in] delete_all_kvno Removes all obsolete entries, without
217 : * recreating the keytab.
218 : * @param[out] _keytab If supplied, returns the keytab
219 : * @param[out] perror_string Error string on failure
220 : *
221 : * @return 0 on success, errno on failure
222 : */
223 241 : krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
224 : krb5_context context,
225 : const char *keytab_name,
226 : const char *samAccountName,
227 : const char *realm,
228 : const char **SPNs,
229 : int num_SPNs,
230 : const char *saltPrincipal,
231 : const char *new_secret,
232 : const char *old_secret,
233 : int kvno,
234 : uint32_t supp_enctypes,
235 : bool delete_all_kvno,
236 : krb5_keytab *_keytab,
237 : const char **perror_string)
238 : {
239 : krb5_keytab keytab;
240 : krb5_error_code ret;
241 241 : bool found_previous = false;
242 : TALLOC_CTX *tmp_ctx;
243 241 : krb5_principal *principals = NULL;
244 241 : uint32_t num_principals = 0;
245 : char *upper_realm;
246 241 : const char *error_string = NULL;
247 :
248 241 : if (keytab_name == NULL) {
249 0 : return ENOENT;
250 : }
251 :
252 241 : ret = krb5_kt_resolve(context, keytab_name, &keytab);
253 241 : if (ret) {
254 0 : *perror_string = smb_get_krb5_error_message(context,
255 : ret, parent_ctx);
256 0 : return ret;
257 : }
258 :
259 241 : DEBUG(5, ("Opened keytab %s\n", keytab_name));
260 :
261 241 : tmp_ctx = talloc_new(parent_ctx);
262 241 : if (!tmp_ctx) {
263 0 : *perror_string = talloc_strdup(parent_ctx,
264 : "Failed to allocate memory context");
265 0 : return ENOMEM;
266 : }
267 :
268 241 : upper_realm = strupper_talloc(tmp_ctx, realm);
269 241 : if (upper_realm == NULL) {
270 0 : *perror_string = talloc_strdup(parent_ctx,
271 : "Cannot allocate memory to upper case realm");
272 0 : talloc_free(tmp_ctx);
273 0 : return ENOMEM;
274 : }
275 :
276 241 : ret = smb_krb5_create_principals_array(tmp_ctx,
277 : context,
278 : samAccountName,
279 : upper_realm,
280 : num_SPNs,
281 : SPNs,
282 : &num_principals,
283 : &principals,
284 : &error_string);
285 241 : if (ret != 0) {
286 0 : *perror_string = talloc_asprintf(parent_ctx,
287 : "Failed to load principals from ldb message: %s\n",
288 : error_string);
289 0 : goto done;
290 : }
291 :
292 241 : ret = smb_krb5_remove_obsolete_keytab_entries(tmp_ctx,
293 : context,
294 : keytab,
295 : num_principals,
296 : principals,
297 : kvno,
298 : &found_previous,
299 : &error_string);
300 241 : if (ret != 0) {
301 0 : *perror_string = talloc_asprintf(parent_ctx,
302 : "Failed to remove old principals from keytab: %s\n",
303 : error_string);
304 0 : goto done;
305 : }
306 :
307 241 : if (!delete_all_kvno) {
308 : /* Create a new keytab. If during the cleanout we found
309 : * entries for kvno -1, then don't try and duplicate them.
310 : * Otherwise, add kvno, and kvno -1 */
311 239 : if (saltPrincipal == NULL) {
312 0 : *perror_string = talloc_strdup(parent_ctx,
313 : "No saltPrincipal provided");
314 0 : ret = EINVAL;
315 0 : goto done;
316 : }
317 :
318 239 : ret = create_keytab(tmp_ctx,
319 : samAccountName, upper_realm, saltPrincipal,
320 : kvno, new_secret, old_secret,
321 : supp_enctypes,
322 : num_principals,
323 : principals,
324 : context, keytab,
325 239 : found_previous ? false : true,
326 : &error_string);
327 239 : if (ret) {
328 0 : *perror_string = talloc_steal(parent_ctx, error_string);
329 : }
330 : }
331 :
332 448 : if (ret == 0 && _keytab != NULL) {
333 : /* caller wants the keytab handle back */
334 67 : *_keytab = keytab;
335 : }
336 :
337 381 : done:
338 241 : keytab_principals_free(context, num_principals, principals);
339 241 : if (ret != 0 || _keytab == NULL) {
340 174 : krb5_kt_close(context, keytab);
341 : }
342 241 : talloc_free(tmp_ctx);
343 241 : return ret;
344 : }
345 :
346 : /**
347 : * @brief Wrapper around smb_krb5_update_keytab() for creating an in-memory keytab
348 : *
349 : * @param[in] parent_ctx Talloc memory context
350 : * @param[in] context Kerberos context
351 : * @param[in] new_secret New password
352 : * @param[in] samAccountName User account to update
353 : * @param[in] realm Kerberos realm
354 : * @param[in] salt_principal Salt used for AES encryption.
355 : * Required, unless delete_all_kvno is set.
356 : * @param[in] kvno Current key version number
357 : * @param[out] keytab If supplied, returns the keytab
358 : * @param[out] keytab_name Returns the created keytab name
359 : *
360 : * @return 0 on success, errno on failure
361 : */
362 67 : krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
363 : krb5_context context,
364 : const char *new_secret,
365 : const char *samAccountName,
366 : const char *realm,
367 : const char *salt_principal,
368 : int kvno,
369 : krb5_keytab *keytab,
370 : const char **keytab_name)
371 : {
372 : krb5_error_code ret;
373 67 : TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
374 : const char *rand_string;
375 67 : const char *error_string = NULL;
376 67 : if (!mem_ctx) {
377 0 : return ENOMEM;
378 : }
379 :
380 67 : rand_string = generate_random_str(mem_ctx, 16);
381 67 : if (!rand_string) {
382 0 : talloc_free(mem_ctx);
383 0 : return ENOMEM;
384 : }
385 :
386 67 : *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
387 67 : if (*keytab_name == NULL) {
388 0 : talloc_free(mem_ctx);
389 0 : return ENOMEM;
390 : }
391 :
392 67 : ret = smb_krb5_update_keytab(mem_ctx, context,
393 : *keytab_name, samAccountName, realm,
394 : NULL, 0, salt_principal, new_secret, NULL,
395 : kvno, ENC_ALL_TYPES,
396 : false, keytab, &error_string);
397 67 : if (ret == 0) {
398 67 : talloc_steal(parent_ctx, *keytab_name);
399 : } else {
400 0 : DEBUG(0, ("Failed to create in-memory keytab: %s\n",
401 : error_string));
402 0 : *keytab_name = NULL;
403 : }
404 67 : talloc_free(mem_ctx);
405 67 : return ret;
406 : }
|