s3-dcerpc: add krb5 helpers
[Samba.git] / source3 / librpc / rpc / dcerpc_krb5.c
blobeb59863abaf0d336fc0a0c95b790cfe851548f7a
1 /*
2 * GSSAPI Security Extensions
3 * Krb5 helpers
4 * Copyright (C) Simo Sorce 2010.
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.
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.
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/>.
20 #include "includes.h"
21 #include "smb_krb5.h"
22 #include "secrets.h"
24 #ifdef HAVE_KRB5
26 static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
28 krb5_error_code ret;
29 krb5_kt_cursor kt_cursor = NULL;
30 krb5_keytab_entry kt_entry;
32 ZERO_STRUCT(kt_entry);
34 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
35 if (ret == KRB5_KT_END || ret == ENOENT ) {
36 /* no entries */
37 return 0;
40 ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
41 while (ret == 0) {
43 /* we need to close and reopen enumeration because we modify
44 * the keytab */
45 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
46 if (ret) {
47 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
48 "failed (%s)\n", error_message(ret)));
49 goto out;
52 /* remove the entry */
53 ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
54 if (ret) {
55 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
56 "failed (%s)\n", error_message(ret)));
57 goto out;
59 ret = smb_krb5_kt_free_entry(krbctx, &kt_entry);
60 ZERO_STRUCT(kt_entry);
62 /* now reopen */
63 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
64 if (ret) {
65 DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
66 "(%s)\n", error_message(ret)));
67 goto out;
70 ret = krb5_kt_next_entry(krbctx, keytab,
71 &kt_entry, &kt_cursor);
74 if (ret != KRB5_KT_END && ret != ENOENT) {
75 DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
76 error_message(ret)));
79 ret = 0;
81 out:
82 return ret;
85 static krb5_error_code get_host_principal(krb5_context krbctx,
86 krb5_principal *host_princ)
88 krb5_error_code ret;
89 char *host_princ_s = NULL;
90 int err;
92 err = asprintf(&host_princ_s, "%s$@%s", global_myname(), lp_realm());
93 if (err == -1) {
94 return -1;
97 strlower_m(host_princ_s);
98 ret = smb_krb5_parse_name(krbctx, host_princ_s, host_princ);
99 if (ret) {
100 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
101 "failed (%s)\n",
102 host_princ_s, error_message(ret)));
105 SAFE_FREE(host_princ_s);
106 return ret;
109 static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
110 krb5_keytab keytab,
111 krb5_principal princ,
112 krb5_kvno vno,
113 krb5_data *password)
115 krb5_error_code ret;
116 krb5_enctype *enctypes;
117 krb5_keytab_entry kt_entry;
118 unsigned int i;
120 ret = krb5_get_permitted_enctypes(krbctx, &enctypes);
121 if (ret) {
122 DEBUG(1, (__location__
123 ": Can't determine permitted enctypes!\n"));
124 return ret;
127 for (i = 0; enctypes[i]; i++) {
128 krb5_keyblock *key = NULL;
130 if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
131 ret = ENOMEM;
132 goto out;
135 if (create_kerberos_key_from_string(krbctx, princ,
136 password, key,
137 enctypes[i], false)) {
138 DEBUG(10, ("Failed to create key for enctype %d "
139 "(error: %s)\n",
140 enctypes[i], error_message(ret)));
141 SAFE_FREE(key);
142 continue;
145 kt_entry.principal = princ;
146 kt_entry.vno = vno;
147 kt_entry.key = *key;
149 ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
150 if (ret) {
151 DEBUG(1, (__location__ ": Failed to add entry to "
152 "keytab for enctype %d (error: %s)\n",
153 enctypes[i], error_message(ret)));
154 krb5_free_keyblock(krbctx, key);
155 goto out;
158 krb5_free_keyblock(krbctx, key);
161 ret = 0;
163 out:
164 SAFE_FREE(enctypes);
165 return ret;
168 #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
169 #define CLEARTEXT_PRIV_ENCTYPE -99
171 static krb5_error_code get_mem_keytab_from_secrets(krb5_context krbctx,
172 krb5_keytab *keytab)
174 krb5_error_code ret;
175 char *pwd = NULL;
176 size_t pwd_len;
177 krb5_kt_cursor kt_cursor = NULL;
178 krb5_keytab_entry kt_entry;
179 krb5_data password;
180 krb5_principal princ = NULL;
181 krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
182 char *pwd_old = NULL;
184 if (!secrets_init()) {
185 DEBUG(1, (__location__ ": secrets_init failed\n"));
186 return KRB5_CONFIG_CANTOPEN;
189 pwd = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
190 if (!pwd) {
191 DEBUG(2, (__location__ ": failed to fetch machine password\n"));
192 return KRB5_LIBOS_CANTREADPWD;
194 pwd_len = strlen(pwd);
196 if (*keytab == NULL) {
197 /* create memory keytab */
198 ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
199 if (ret) {
200 DEBUG(1, (__location__ ": Failed to get memory "
201 "keytab!\n"));
202 return ret;
206 ZERO_STRUCT(kt_entry);
208 /* check if the keytab already has any entry */
209 ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
210 if (ret != KRB5_KT_END && ret != ENOENT ) {
211 /* check if we have our special enctype used to hold
212 * the clear text password. If so, check it out so that
213 * we can verify if the keytab needs to be upgraded */
214 while ((ret = krb5_kt_next_entry(krbctx, *keytab,
215 &kt_entry, &kt_cursor)) == 0) {
216 if (kt_entry.key.enctype == CLEARTEXT_PRIV_ENCTYPE) {
217 break;
219 smb_krb5_kt_free_entry(krbctx, &kt_entry);
220 ZERO_STRUCT(kt_entry);
223 if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
224 /* Error parsing keytab */
225 DEBUG(1, (__location__ ": Failed to parse memory "
226 "keytab!\n"));
227 goto out;
230 if (ret == 0) {
231 /* found private entry,
232 * check if keytab is up to date */
234 if ((pwd_len == kt_entry.key.length) &&
235 (memcmp(kt_entry.key.contents,
236 pwd, pwd_len) == 0)) {
237 /* keytab is already up to date, return */
238 smb_krb5_kt_free_entry(krbctx, &kt_entry);
239 goto out;
242 smb_krb5_kt_free_entry(krbctx, &kt_entry);
243 ZERO_STRUCT(kt_entry);
246 /* flush keytab, we need to regen it */
247 ret = flush_keytab(krbctx, *keytab);
248 if (ret) {
249 DEBUG(1, (__location__ ": Failed to flush "
250 "memory keytab!\n"));
251 goto out;
256 if (kt_cursor) {
257 /* stop enumeration and free cursor */
258 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
259 kt_cursor = NULL;
262 /* keytab is not up to date, fill it up */
264 ret = get_host_principal(krbctx, &princ);
265 if (ret) {
266 DEBUG(1, (__location__ ": Failed to get host principal!\n"));
267 goto out;
270 password.data = pwd;
271 password.length = pwd_len;
272 ret = fill_keytab_from_password(krbctx, *keytab,
273 princ, kvno, &password);
274 if (ret) {
275 DEBUG(1, (__location__ ": Failed to fill memory keytab!\n"));
276 goto out;
279 pwd_old = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
280 if (!pwd_old) {
281 DEBUG(10, (__location__ ": no prev machine password\n"));
282 } else {
283 password.data = pwd_old;
284 password.length = strlen(pwd_old);
285 ret = fill_keytab_from_password(krbctx, *keytab,
286 princ, kvno -1, &password);
287 if (ret) {
288 DEBUG(1, (__location__
289 ": Failed to fill memory keytab!\n"));
290 goto out;
294 /* add our private enctype + cleartext password so that we can
295 * update the keytab if secrets change later on */
296 ZERO_STRUCT(kt_entry);
297 kt_entry.principal = princ;
298 kt_entry.vno = 0;
299 kt_entry.key.enctype = CLEARTEXT_PRIV_ENCTYPE;
300 kt_entry.key.length = pwd_len;
301 kt_entry.key.contents = (uint8_t *)pwd;
303 ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
304 if (ret) {
305 DEBUG(1, (__location__ ": Failed to add entry to "
306 "keytab for private enctype (%d) (error: %s)\n",
307 CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
308 goto out;
311 ret = 0;
313 out:
314 SAFE_FREE(pwd);
315 SAFE_FREE(pwd_old);
317 if (kt_cursor) {
318 /* stop enumeration and free cursor */
319 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
320 kt_cursor = NULL;
323 if (princ) {
324 krb5_free_principal(krbctx, princ);
327 if (ret) {
328 if (*keytab) {
329 krb5_kt_close(krbctx, *keytab);
330 *keytab = NULL;
334 return ret;
337 static krb5_error_code get_mem_keytab_from_system_keytab(krb5_context krbctx,
338 krb5_keytab *keytab,
339 bool verify)
341 return KRB5_KT_NOTFOUND;
344 krb5_error_code smb_krb5_get_server_keytab(krb5_context krbctx,
345 krb5_keytab *keytab)
347 krb5_error_code ret;
349 *keytab = NULL;
351 switch (lp_kerberos_method()) {
352 default:
353 case KERBEROS_VERIFY_SECRETS:
354 ret = get_mem_keytab_from_secrets(krbctx, keytab);
355 break;
356 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
357 ret = get_mem_keytab_from_system_keytab(krbctx, keytab, true);
358 break;
359 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
360 /* just use whatever keytab is configured */
361 ret = get_mem_keytab_from_system_keytab(krbctx, keytab, false);
362 break;
363 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
364 ret = get_mem_keytab_from_secrets(krbctx, keytab);
365 if (ret) {
366 DEBUG(3, (__location__ ": Warning! Unable to set mem "
367 "keytab from secrets!\n"));
369 /* Now append system keytab keys too */
370 ret = get_mem_keytab_from_system_keytab(krbctx, keytab, true);
371 if (ret) {
372 DEBUG(3, (__location__ ": Warning! Unable to set mem "
373 "keytab from secrets!\n"));
375 break;
378 return ret;
381 #endif /* HAVE_KRB5 */