s4:auth/kerberos: improve error message in kerberos_pac_to_user_info_dc()
[sfrench/samba-autobuild/.git] / source4 / auth / kerberos / srv_keytab.c
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 #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"
30
31 static void keytab_principals_free(krb5_context context,
32                                    uint32_t num_principals,
33                                    krb5_principal *set)
34 {
35         uint32_t i;
36
37         for (i = 0; i < num_principals; i++) {
38                 krb5_free_principal(context, set[i]);
39         }
40 }
41
42 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
43                                        uint32_t num_principals,
44                                        krb5_principal *principals,
45                                        krb5_principal salt_princ,
46                                        int kvno,
47                                        const char *password_s,
48                                        krb5_context context,
49                                        krb5_enctype *enctypes,
50                                        krb5_keytab keytab,
51                                        const char **error_string)
52 {
53         unsigned int i, p;
54         krb5_error_code ret;
55         krb5_data password;
56         char *unparsed;
57
58         password.data = discard_const_p(char, password_s);
59         password.length = strlen(password_s);
60
61         for (i = 0; enctypes[i]; i++) {
62                 krb5_keytab_entry entry;
63
64                 ZERO_STRUCT(entry);
65
66                 ret = smb_krb5_create_key_from_string(context,
67                                                       salt_princ,
68                                                       NULL,
69                                                       &password,
70                                                       enctypes[i],
71                                                       KRB5_KT_KEY(&entry));
72                 if (ret != 0) {
73                         *error_string = talloc_strdup(parent_ctx,
74                                                       "Failed to create key from string");
75                         return ret;
76                 }
77
78                 entry.vno = kvno;
79
80                 for (p = 0; p < num_principals; p++) {
81                         unparsed = NULL;
82                         entry.principal = principals[p];
83                         ret = krb5_kt_add_entry(context, keytab, &entry);
84                         if (ret != 0) {
85                                 char *k5_error_string =
86                                         smb_get_krb5_error_message(context,
87                                                                    ret, NULL);
88                                 krb5_unparse_name(context,
89                                                 principals[p], &unparsed);
90                                 *error_string = talloc_asprintf(parent_ctx,
91                                         "Failed to add enctype %d entry for "
92                                         "%s(kvno %d) to keytab: %s\n",
93                                         (int)enctypes[i], unparsed,
94                                         kvno, k5_error_string);
95
96                                 free(unparsed);
97                                 talloc_free(k5_error_string);
98                                 krb5_free_keyblock_contents(context,
99                                                             KRB5_KT_KEY(&entry));
100                                 return ret;
101                         }
102
103                         DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
104                                   kvno, (int)enctypes[i]));
105                 }
106                 krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
107         }
108         return 0;
109 }
110
111 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
112                                      const char *samAccountName,
113                                      const char *realm,
114                                      const char *saltPrincipal,
115                                      int kvno,
116                                      const char *new_secret,
117                                      const char *old_secret,
118                                      uint32_t supp_enctypes,
119                                      uint32_t num_principals,
120                                      krb5_principal *principals,
121                                      krb5_context context,
122                                      krb5_keytab keytab,
123                                      bool add_old,
124                                      const char **perror_string)
125 {
126         krb5_error_code ret;
127         krb5_principal salt_princ = NULL;
128         krb5_enctype *enctypes;
129         TALLOC_CTX *mem_ctx;
130         const char *error_string = NULL;
131
132         if (!new_secret) {
133                 /* There is no password here, so nothing to do */
134                 return 0;
135         }
136
137         mem_ctx = talloc_new(parent_ctx);
138         if (!mem_ctx) {
139                 *perror_string = talloc_strdup(parent_ctx,
140                         "unable to allocate tmp_ctx for create_keytab");
141                 return ENOMEM;
142         }
143
144         /* The salt used to generate these entries may be different however,
145          * fetch that */
146         ret = krb5_parse_name(context, saltPrincipal, &salt_princ);
147         if (ret) {
148                 *perror_string = smb_get_krb5_error_message(context,
149                                                            ret,
150                                                            parent_ctx);
151                 talloc_free(mem_ctx);
152                 return ret;
153         }
154
155         ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
156         if (ret) {
157                 *perror_string = talloc_asprintf(parent_ctx,
158                                         "create_keytab: generating list of "
159                                         "encryption types failed (%s)\n",
160                                         smb_get_krb5_error_message(context,
161                                                                 ret, mem_ctx));
162                 goto done;
163         }
164
165         ret = keytab_add_keys(mem_ctx,
166                               num_principals,
167                               principals,
168                               salt_princ, kvno, new_secret,
169                               context, enctypes, keytab, &error_string);
170         if (ret) {
171                 *perror_string = talloc_steal(parent_ctx, error_string);
172                 goto done;
173         }
174
175         if (old_secret && add_old && kvno != 0) {
176                 ret = keytab_add_keys(mem_ctx,
177                                       num_principals,
178                                       principals,
179                                       salt_princ, kvno - 1, old_secret,
180                                       context, enctypes, keytab, &error_string);
181                 if (ret) {
182                         *perror_string = talloc_steal(parent_ctx, error_string);
183                 }
184         }
185
186 done:
187         krb5_free_principal(context, salt_princ);
188         talloc_free(mem_ctx);
189         return ret;
190 }
191
192 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
193                                 krb5_context context,
194                                 const char *keytab_name,
195                                 const char *samAccountName,
196                                 const char *realm,
197                                 const char **SPNs,
198                                 int num_SPNs,
199                                 const char *saltPrincipal,
200                                 const char *new_secret,
201                                 const char *old_secret,
202                                 int kvno,
203                                 uint32_t supp_enctypes,
204                                 bool delete_all_kvno,
205                                 krb5_keytab *_keytab,
206                                 const char **perror_string)
207 {
208         krb5_keytab keytab;
209         krb5_error_code ret;
210         bool found_previous = false;
211         TALLOC_CTX *tmp_ctx;
212         krb5_principal *principals = NULL;
213         uint32_t num_principals = 0;
214         char *upper_realm;
215         const char *error_string = NULL;
216
217         if (keytab_name == NULL) {
218                 return ENOENT;
219         }
220
221         if (saltPrincipal == NULL) {
222                 *perror_string = talloc_strdup(parent_ctx,
223                                               "No saltPrincipal provided");
224                 return EINVAL;
225         }
226
227         ret = krb5_kt_resolve(context, keytab_name, &keytab);
228         if (ret) {
229                 *perror_string = smb_get_krb5_error_message(context,
230                                                            ret, parent_ctx);
231                 return ret;
232         }
233
234         DEBUG(5, ("Opened keytab %s\n", keytab_name));
235
236         tmp_ctx = talloc_new(parent_ctx);
237         if (!tmp_ctx) {
238                 *perror_string = talloc_strdup(parent_ctx,
239                                               "Failed to allocate memory context");
240                 return ENOMEM;
241         }
242
243         upper_realm = strupper_talloc(tmp_ctx, realm);
244         if (upper_realm == NULL) {
245                 *perror_string = talloc_strdup(parent_ctx,
246                                               "Cannot allocate memory to upper case realm");
247                 talloc_free(tmp_ctx);
248                 return ENOMEM;
249         }
250
251         ret = smb_krb5_create_principals_array(tmp_ctx,
252                                                context,
253                                                samAccountName,
254                                                upper_realm,
255                                                num_SPNs,
256                                                SPNs,
257                                                &num_principals,
258                                                &principals,
259                                                &error_string);
260         if (ret != 0) {
261                 *perror_string = talloc_asprintf(parent_ctx,
262                         "Failed to load principals from ldb message: %s\n",
263                         error_string);
264                 goto done;
265         }
266
267         ret = smb_krb5_remove_obsolete_keytab_entries(tmp_ctx,
268                                                       context,
269                                                       keytab,
270                                                       num_principals,
271                                                       principals,
272                                                       kvno,
273                                                       &found_previous,
274                                                       &error_string);
275         if (ret != 0) {
276                 *perror_string = talloc_asprintf(parent_ctx,
277                         "Failed to remove old principals from keytab: %s\n",
278                         error_string);
279                 goto done;
280         }
281
282         if (!delete_all_kvno) {
283                 /* Create a new keytab.  If during the cleanout we found
284                  * entires for kvno -1, then don't try and duplicate them.
285                  * Otherwise, add kvno, and kvno -1 */
286
287                 ret = create_keytab(tmp_ctx,
288                                     samAccountName, upper_realm, saltPrincipal,
289                                     kvno, new_secret, old_secret,
290                                     supp_enctypes,
291                                     num_principals,
292                                     principals,
293                                     context, keytab,
294                                     found_previous ? false : true,
295                                     &error_string);
296                 if (ret) {
297                         *perror_string = talloc_steal(parent_ctx, error_string);
298                 }
299         }
300
301         if (ret == 0 && _keytab != NULL) {
302                 /* caller wants the keytab handle back */
303                 *_keytab = keytab;
304         }
305
306 done:
307         keytab_principals_free(context, num_principals, principals);
308         if (ret != 0 || _keytab == NULL) {
309                 krb5_kt_close(context, keytab);
310         }
311         talloc_free(tmp_ctx);
312         return ret;
313 }
314
315 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
316                                 krb5_context context,
317                                 const char *new_secret,
318                                 const char *samAccountName,
319                                 const char *realm,
320                                 const char *salt_principal,
321                                 int kvno,
322                                 krb5_keytab *keytab,
323                                 const char **keytab_name)
324 {
325         krb5_error_code ret;
326         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
327         const char *rand_string;
328         const char *error_string = NULL;
329         if (!mem_ctx) {
330                 return ENOMEM;
331         }
332
333         rand_string = generate_random_str(mem_ctx, 16);
334         if (!rand_string) {
335                 talloc_free(mem_ctx);
336                 return ENOMEM;
337         }
338
339         *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
340         if (*keytab_name == NULL) {
341                 talloc_free(mem_ctx);
342                 return ENOMEM;
343         }
344
345         ret = smb_krb5_update_keytab(mem_ctx, context,
346                                      *keytab_name, samAccountName, realm,
347                                      NULL, 0, salt_principal, new_secret, NULL,
348                                      kvno, ENC_ALL_TYPES,
349                                      false, keytab, &error_string);
350         if (ret == 0) {
351                 talloc_steal(parent_ctx, *keytab_name);
352         } else {
353                 DEBUG(0, ("Failed to create in-memory keytab: %s\n",
354                           error_string));
355                 *keytab_name = NULL;
356         }
357         talloc_free(mem_ctx);
358         return ret;
359 }