source: trunk/server/source3/librpc/crypto/gse_krb5.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 9.9 KB
Line 
1/*
2 * GSSAPI Security Extensions
3 * Krb5 helpers
4 * Copyright (C) Simo Sorce 2010.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "includes.h"
21#include "smb_krb5.h"
22#include "secrets.h"
23#include "gse_krb5.h"
24
25#ifdef HAVE_KRB5
26
27static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
28{
29 krb5_error_code ret;
30 krb5_kt_cursor kt_cursor;
31 krb5_keytab_entry kt_entry;
32
33 ZERO_STRUCT(kt_entry);
34
35 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
36 if (ret == KRB5_KT_END || ret == ENOENT ) {
37 /* no entries */
38 return 0;
39 }
40
41 ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
42 while (ret == 0) {
43
44 /* we need to close and reopen enumeration because we modify
45 * the keytab */
46 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
47 if (ret) {
48 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
49 "failed (%s)\n", error_message(ret)));
50 goto out;
51 }
52
53 /* remove the entry */
54 ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
55 if (ret) {
56 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
57 "failed (%s)\n", error_message(ret)));
58 goto out;
59 }
60 ret = smb_krb5_kt_free_entry(krbctx, &kt_entry);
61 ZERO_STRUCT(kt_entry);
62
63 /* now reopen */
64 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
65 if (ret) {
66 DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
67 "(%s)\n", error_message(ret)));
68 goto out;
69 }
70
71 ret = krb5_kt_next_entry(krbctx, keytab,
72 &kt_entry, &kt_cursor);
73 }
74
75 if (ret != KRB5_KT_END && ret != ENOENT) {
76 DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
77 error_message(ret)));
78 }
79
80 ret = 0;
81
82out:
83 return ret;
84}
85
86static krb5_error_code get_host_principal(krb5_context krbctx,
87 krb5_principal *host_princ)
88{
89 krb5_error_code ret;
90 char *host_princ_s = NULL;
91 int err;
92
93 err = asprintf(&host_princ_s, "%s$@%s", global_myname(), lp_realm());
94 if (err == -1) {
95 return -1;
96 }
97
98 strlower_m(host_princ_s);
99 ret = smb_krb5_parse_name(krbctx, host_princ_s, host_princ);
100 if (ret) {
101 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
102 "failed (%s)\n",
103 host_princ_s, error_message(ret)));
104 }
105
106 SAFE_FREE(host_princ_s);
107 return ret;
108}
109
110static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
111 krb5_keytab keytab,
112 krb5_principal princ,
113 krb5_kvno vno,
114 krb5_data *password)
115{
116 krb5_error_code ret;
117 krb5_enctype *enctypes;
118 krb5_keytab_entry kt_entry;
119 unsigned int i;
120
121 ret = get_kerberos_allowed_etypes(krbctx, &enctypes);
122 if (ret) {
123 DEBUG(1, (__location__
124 ": Can't determine permitted enctypes!\n"));
125 return ret;
126 }
127
128 for (i = 0; enctypes[i]; i++) {
129 krb5_keyblock *key = NULL;
130
131 if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
132 ret = ENOMEM;
133 goto out;
134 }
135
136 if (create_kerberos_key_from_string(krbctx, princ,
137 password, key,
138 enctypes[i], false)) {
139 DEBUG(10, ("Failed to create key for enctype %d "
140 "(error: %s)\n",
141 enctypes[i], error_message(ret)));
142 SAFE_FREE(key);
143 continue;
144 }
145
146 kt_entry.principal = princ;
147 kt_entry.vno = vno;
148 *(KRB5_KT_KEY(&kt_entry)) = *key;
149
150 ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
151 if (ret) {
152 DEBUG(1, (__location__ ": Failed to add entry to "
153 "keytab for enctype %d (error: %s)\n",
154 enctypes[i], error_message(ret)));
155 krb5_free_keyblock(krbctx, key);
156 goto out;
157 }
158
159 krb5_free_keyblock(krbctx, key);
160 }
161
162 ret = 0;
163
164out:
165 SAFE_FREE(enctypes);
166 return ret;
167}
168
169#define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
170#define CLEARTEXT_PRIV_ENCTYPE -99
171
172static krb5_error_code get_mem_keytab_from_secrets(krb5_context krbctx,
173 krb5_keytab *keytab)
174{
175 krb5_error_code ret;
176 char *pwd = NULL;
177 size_t pwd_len;
178 krb5_kt_cursor kt_cursor;
179 krb5_keytab_entry kt_entry;
180 krb5_data password;
181 krb5_principal princ = NULL;
182 krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
183 char *pwd_old = NULL;
184
185 if (!secrets_init()) {
186 DEBUG(1, (__location__ ": secrets_init failed\n"));
187 return KRB5_CONFIG_CANTOPEN;
188 }
189
190 pwd = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
191 if (!pwd) {
192 DEBUG(2, (__location__ ": failed to fetch machine password\n"));
193 return KRB5_LIBOS_CANTREADPWD;
194 }
195 pwd_len = strlen(pwd);
196
197 if (*keytab == NULL) {
198 /* create memory keytab */
199 ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
200 if (ret) {
201 DEBUG(1, (__location__ ": Failed to get memory "
202 "keytab!\n"));
203 return ret;
204 }
205 }
206
207 ZERO_STRUCT(kt_entry);
208 ZERO_STRUCT(kt_cursor);
209
210 /* check if the keytab already has any entry */
211 ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
212 if (ret != KRB5_KT_END && ret != ENOENT ) {
213 /* check if we have our special enctype used to hold
214 * the clear text password. If so, check it out so that
215 * we can verify if the keytab needs to be upgraded */
216 while ((ret = krb5_kt_next_entry(krbctx, *keytab,
217 &kt_entry, &kt_cursor)) == 0) {
218 if (smb_get_enctype_from_kt_entry(&kt_entry) == CLEARTEXT_PRIV_ENCTYPE) {
219 break;
220 }
221 smb_krb5_kt_free_entry(krbctx, &kt_entry);
222 ZERO_STRUCT(kt_entry);
223 }
224
225 if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
226 /* Error parsing keytab */
227 DEBUG(1, (__location__ ": Failed to parse memory "
228 "keytab!\n"));
229 goto out;
230 }
231
232 if (ret == 0) {
233 /* found private entry,
234 * check if keytab is up to date */
235
236 if ((pwd_len == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
237 (memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
238 pwd, pwd_len) == 0)) {
239 /* keytab is already up to date, return */
240 smb_krb5_kt_free_entry(krbctx, &kt_entry);
241 goto out;
242 }
243
244 smb_krb5_kt_free_entry(krbctx, &kt_entry);
245 ZERO_STRUCT(kt_entry);
246
247
248 /* flush keytab, we need to regen it */
249 ret = flush_keytab(krbctx, *keytab);
250 if (ret) {
251 DEBUG(1, (__location__ ": Failed to flush "
252 "memory keytab!\n"));
253 goto out;
254 }
255 }
256 }
257
258 {
259 krb5_kt_cursor zero_csr;
260 ZERO_STRUCT(zero_csr);
261 if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && *keytab) {
262 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
263 }
264 }
265
266 /* keytab is not up to date, fill it up */
267
268 ret = get_host_principal(krbctx, &princ);
269 if (ret) {
270 DEBUG(1, (__location__ ": Failed to get host principal!\n"));
271 goto out;
272 }
273
274 password.data = pwd;
275 password.length = pwd_len;
276 ret = fill_keytab_from_password(krbctx, *keytab,
277 princ, kvno, &password);
278 if (ret) {
279 DEBUG(1, (__location__ ": Failed to fill memory keytab!\n"));
280 goto out;
281 }
282
283 pwd_old = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
284 if (!pwd_old) {
285 DEBUG(10, (__location__ ": no prev machine password\n"));
286 } else {
287 password.data = pwd_old;
288 password.length = strlen(pwd_old);
289 ret = fill_keytab_from_password(krbctx, *keytab,
290 princ, kvno -1, &password);
291 if (ret) {
292 DEBUG(1, (__location__
293 ": Failed to fill memory keytab!\n"));
294 goto out;
295 }
296 }
297
298 /* add our private enctype + cleartext password so that we can
299 * update the keytab if secrets change later on */
300 ZERO_STRUCT(kt_entry);
301 kt_entry.principal = princ;
302 kt_entry.vno = 0;
303
304 KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
305 KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = pwd_len;
306 KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = (uint8_t *)pwd;
307
308 ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
309 if (ret) {
310 DEBUG(1, (__location__ ": Failed to add entry to "
311 "keytab for private enctype (%d) (error: %s)\n",
312 CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
313 goto out;
314 }
315
316 ret = 0;
317
318out:
319 SAFE_FREE(pwd);
320 SAFE_FREE(pwd_old);
321
322 {
323 krb5_kt_cursor zero_csr;
324 ZERO_STRUCT(zero_csr);
325 if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && *keytab) {
326 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
327 }
328 }
329
330 if (princ) {
331 krb5_free_principal(krbctx, princ);
332 }
333
334 if (ret) {
335 if (*keytab) {
336 krb5_kt_close(krbctx, *keytab);
337 *keytab = NULL;
338 }
339 }
340
341 return ret;
342}
343
344static krb5_error_code get_mem_keytab_from_system_keytab(krb5_context krbctx,
345 krb5_keytab *keytab,
346 bool verify)
347{
348 return KRB5_KT_NOTFOUND;
349}
350
351krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
352 krb5_keytab *keytab)
353{
354 krb5_error_code ret;
355
356 *keytab = NULL;
357
358 switch (lp_kerberos_method()) {
359 default:
360 case KERBEROS_VERIFY_SECRETS:
361 ret = get_mem_keytab_from_secrets(krbctx, keytab);
362 break;
363 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
364 ret = get_mem_keytab_from_system_keytab(krbctx, keytab, true);
365 break;
366 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
367 /* just use whatever keytab is configured */
368 ret = get_mem_keytab_from_system_keytab(krbctx, keytab, false);
369 break;
370 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
371 ret = get_mem_keytab_from_secrets(krbctx, keytab);
372 if (ret) {
373 DEBUG(3, (__location__ ": Warning! Unable to set mem "
374 "keytab from secrets!\n"));
375 }
376 /* Now append system keytab keys too */
377 ret = get_mem_keytab_from_system_keytab(krbctx, keytab, true);
378 if (ret) {
379 DEBUG(3, (__location__ ": Warning! Unable to set mem "
380 "keytab from secrets!\n"));
381 }
382 break;
383 }
384
385 return ret;
386}
387
388#endif /* HAVE_KRB5 */
Note: See TracBrowser for help on using the repository browser.