s3:libsmb: allow store_cldap_reply() to work with a ipv6 response
[samba.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  * @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/credentials/credentials_krb5.h"
34 #include "auth/kerberos/kerberos.h"
35 #include "auth/kerberos/kerberos_util.h"
36 #include "auth/kerberos/kerberos_srv_keytab.h"
37 #include "librpc/gen_ndr/ndr_gmsa.h"
38 #include "dsdb/samdb/samdb.h"
39
40 static void keytab_principals_free(krb5_context context,
41                                    uint32_t num_principals,
42                                    krb5_principal *set)
43 {
44         uint32_t i;
45
46         for (i = 0; i < num_principals; i++) {
47                 krb5_free_principal(context, set[i]);
48         }
49 }
50
51 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
52                                        uint32_t num_principals,
53                                        krb5_principal *principals,
54                                        krb5_principal salt_princ,
55                                        int kvno,
56                                        const char *password_s,
57                                        krb5_context context,
58                                        krb5_enctype *enctypes,
59                                        krb5_keytab keytab,
60                                        const char **error_string)
61 {
62         unsigned int i, p;
63         krb5_error_code ret;
64         krb5_data password;
65         char *unparsed;
66
67         password.data = discard_const_p(char, password_s);
68         password.length = strlen(password_s);
69
70         for (i = 0; enctypes[i]; i++) {
71                 krb5_keytab_entry entry;
72
73                 ZERO_STRUCT(entry);
74
75                 ret = smb_krb5_create_key_from_string(context,
76                                                       salt_princ,
77                                                       NULL,
78                                                       &password,
79                                                       enctypes[i],
80                                                       KRB5_KT_KEY(&entry));
81                 if (ret != 0) {
82                         *error_string = talloc_strdup(parent_ctx,
83                                                       "Failed to create key from string");
84                         return ret;
85                 }
86
87                 entry.vno = kvno;
88
89                 for (p = 0; p < num_principals; p++) {
90                         bool found = false;
91
92                         unparsed = NULL;
93                         entry.principal = principals[p];
94
95                         ret = smb_krb5_is_exact_entry_in_keytab(parent_ctx,
96                                                                 context,
97                                                                 keytab,
98                                                                 &entry,
99                                                                 &found,
100                                                                 error_string);
101                         if (ret != 0) {
102                                 krb5_free_keyblock_contents(context,
103                                                             KRB5_KT_KEY(&entry));
104                                 return ret;
105                         }
106
107                         /*
108                          * Do not add the exact same key twice, this
109                          * will allow "samba-tool domain exportkeytab"
110                          * to refresh a keytab rather than infinitely
111                          * extend it
112                          */
113                         if (found) {
114                                 continue;
115                         }
116
117                         ret = krb5_kt_add_entry(context, keytab, &entry);
118                         if (ret != 0) {
119                                 char *k5_error_string =
120                                         smb_get_krb5_error_message(context,
121                                                                    ret, NULL);
122                                 krb5_unparse_name(context,
123                                                 principals[p], &unparsed);
124                                 *error_string = talloc_asprintf(parent_ctx,
125                                         "Failed to add enctype %d entry for "
126                                         "%s(kvno %d) to keytab: %s\n",
127                                         (int)enctypes[i], unparsed,
128                                         kvno, k5_error_string);
129
130                                 free(unparsed);
131                                 talloc_free(k5_error_string);
132                                 krb5_free_keyblock_contents(context,
133                                                             KRB5_KT_KEY(&entry));
134                                 return ret;
135                         }
136
137                         DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
138                                   kvno, (int)enctypes[i]));
139                 }
140                 krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
141         }
142         return 0;
143 }
144
145 /*
146  * This is the inner part of smb_krb5_update_keytab on an open keytab
147  * and without the deletion
148  */
149 static krb5_error_code smb_krb5_fill_keytab(TALLOC_CTX *parent_ctx,
150                                             const char *saltPrincipal,
151                                             int kvno,
152                                             const char *new_secret,
153                                             const char *old_secret,
154                                             uint32_t supp_enctypes,
155                                             uint32_t num_principals,
156                                             krb5_principal *principals,
157                                             krb5_context context,
158                                             krb5_keytab keytab,
159                                             bool add_old,
160                                             const char **perror_string)
161 {
162         krb5_error_code ret;
163         krb5_principal salt_princ = NULL;
164         krb5_enctype *enctypes;
165         TALLOC_CTX *mem_ctx;
166         const char *error_string = NULL;
167
168         if (!new_secret) {
169                 /* There is no password here, so nothing to do */
170                 return 0;
171         }
172
173         mem_ctx = talloc_new(parent_ctx);
174         if (!mem_ctx) {
175                 *perror_string = talloc_strdup(parent_ctx,
176                         "unable to allocate tmp_ctx for smb_krb5_fill_keytab");
177                 return ENOMEM;
178         }
179
180         /* The salt used to generate these entries may be different however,
181          * fetch that */
182         ret = krb5_parse_name(context, saltPrincipal, &salt_princ);
183         if (ret) {
184                 *perror_string = smb_get_krb5_error_message(context,
185                                                            ret,
186                                                            parent_ctx);
187                 talloc_free(mem_ctx);
188                 return ret;
189         }
190
191         ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
192         if (ret) {
193                 *perror_string = talloc_asprintf(parent_ctx,
194                                         "smb_krb5_fill_keytab: generating list of "
195                                         "encryption types failed (%s)\n",
196                                         smb_get_krb5_error_message(context,
197                                                                 ret, mem_ctx));
198                 goto done;
199         }
200
201         ret = keytab_add_keys(mem_ctx,
202                               num_principals,
203                               principals,
204                               salt_princ, kvno, new_secret,
205                               context, enctypes, keytab, &error_string);
206         if (ret) {
207                 *perror_string = talloc_steal(parent_ctx, error_string);
208                 goto done;
209         }
210
211         if (old_secret && add_old && kvno != 0) {
212                 ret = keytab_add_keys(mem_ctx,
213                                       num_principals,
214                                       principals,
215                                       salt_princ, kvno - 1, old_secret,
216                                       context, enctypes, keytab, &error_string);
217                 if (ret) {
218                         *perror_string = talloc_steal(parent_ctx, error_string);
219                 }
220         }
221
222 done:
223         krb5_free_principal(context, salt_princ);
224         talloc_free(mem_ctx);
225         return ret;
226 }
227
228 NTSTATUS smb_krb5_fill_keytab_gmsa_keys(TALLOC_CTX *mem_ctx,
229                                         struct smb_krb5_context *smb_krb5_context,
230                                         krb5_keytab keytab,
231                                         krb5_principal principal,
232                                         struct ldb_context *samdb,
233                                         struct ldb_dn *dn,
234                                         const char **error_string)
235 {
236         const char *gmsa_attrs[] = {
237                 "msDS-ManagedPassword",
238                 "msDS-KeyVersionNumber",
239                 "sAMAccountName",
240                 "msDS-SupportedEncryptionTypes",
241                 NULL
242         };
243
244         NTSTATUS status;
245         struct ldb_message *msg;
246         const struct ldb_val *managed_password_blob;
247         const char *managed_pw_utf8;
248         const char *previous_managed_pw_utf8;
249         const char *username;
250         const char *salt_principal;
251         uint32_t kvno = 0;
252         uint32_t supported_enctypes = 0;
253         krb5_context context = smb_krb5_context->krb5_context;
254         struct cli_credentials *cred = NULL;
255         const char *realm = NULL;
256
257         /*
258          * Search for msDS-ManagedPassword (and other attributes to
259          * avoid a race) as this was not in the original search.
260          */
261         int ret;
262
263         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
264         if (tmp_ctx == NULL) {
265                 return NT_STATUS_NO_MEMORY;
266         }
267
268         ret = dsdb_search_one(samdb,
269                               tmp_ctx,
270                               &msg,
271                               dn,
272                               LDB_SCOPE_BASE,
273                               gmsa_attrs, 0,
274                               "(objectClass=msDS-GroupManagedServiceAccount)");
275
276         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
277                 /*
278                  * Race condition, object has gone, or just wasn't a
279                  * gMSA
280                  */
281                 *error_string = talloc_asprintf(mem_ctx,
282                                                 "Did not find gMSA at %s",
283                                                 ldb_dn_get_linearized(dn));
284                 TALLOC_FREE(tmp_ctx);
285                 return NT_STATUS_NO_SUCH_USER;
286         }
287
288         if (ret != LDB_SUCCESS) {
289                 *error_string = talloc_asprintf(mem_ctx,
290                                                 "Error looking for gMSA at %s: %s",
291                                                 ldb_dn_get_linearized(dn), ldb_errstring(samdb));
292                 TALLOC_FREE(tmp_ctx);
293                 return NT_STATUS_UNSUCCESSFUL;
294         }
295
296         /* Extract out passwords */
297         managed_password_blob = ldb_msg_find_ldb_val(msg, "msDS-ManagedPassword");
298
299         if (managed_password_blob == NULL) {
300                 /*
301                  * No password set on this yet or not readable by this user
302                  */
303                 *error_string = talloc_asprintf(mem_ctx,
304                                                 "Did not find msDS-ManagedPassword at %s",
305                                                 ldb_dn_get_extended_linearized(mem_ctx, msg->dn, 1));
306                 TALLOC_FREE(tmp_ctx);
307                 return NT_STATUS_NO_USER_KEYS;
308         }
309
310         cred = cli_credentials_init(tmp_ctx);
311         if (cred == NULL) {
312                 *error_string = talloc_asprintf(mem_ctx,
313                                                 "Could not allocate cli_credentials for %s",
314                                                 ldb_dn_get_linearized(msg->dn));
315                 TALLOC_FREE(tmp_ctx);
316                 return NT_STATUS_NO_MEMORY;
317         }
318
319         realm = smb_krb5_principal_get_realm(tmp_ctx,
320                                              context,
321                                              principal);
322         if (realm == NULL) {
323                 *error_string = talloc_asprintf(mem_ctx,
324                                                 "Could not allocate copy of realm for %s",
325                                                 ldb_dn_get_linearized(msg->dn));
326                 TALLOC_FREE(tmp_ctx);
327                 return NT_STATUS_NO_MEMORY;
328         }
329
330         cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
331
332         username = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
333         if (username == NULL) {
334                 *error_string = talloc_asprintf(mem_ctx,
335                                                 "No sAMAccountName on %s",
336                                                 ldb_dn_get_linearized(msg->dn));
337                 TALLOC_FREE(tmp_ctx);
338                 return NT_STATUS_INVALID_ACCOUNT_NAME;
339         }
340
341         cli_credentials_set_username(cred, username, CRED_SPECIFIED);
342
343         /*
344          * Note that this value may not be correct, it is updated
345          * after the query that gives us the passwords
346          */
347         kvno = ldb_msg_find_attr_as_uint(msg, "msDS-KeyVersionNumber", 0);
348
349         cli_credentials_set_kvno(cred, kvno);
350
351         supported_enctypes = ldb_msg_find_attr_as_uint(msg,
352                                                        "msDS-SupportedEncryptionTypes",
353                                                        ENC_STRONG_SALTED_TYPES);
354         /*
355          * We trim this down to just the salted AES types, as the
356          * passwords are now wrong for rc4-hmac due to the mapping of
357          * invalid sequences in UTF16_MUNGED -> UTF8 string conversion
358          * within cli_credentials_get_password(). Users using this new
359          * feature won't be using such weak crypto anyway.  If
360          * required we could also set the NT Hash as a key directly,
361          * this is just a limitation of smb_krb5_fill_keytab() taking
362          * a simple string as input.
363          */
364         supported_enctypes &= ENC_STRONG_SALTED_TYPES;
365
366         /* Update the keytab */
367
368         status = cli_credentials_set_gmsa_passwords(cred,
369                                                     managed_password_blob,
370                                                     true /* for keytab */,
371                                                     error_string);
372
373         if (!NT_STATUS_IS_OK(status)) {
374                 *error_string = talloc_asprintf(mem_ctx,
375                                                 "Could not parse gMSA passwords on %s: %s",
376                                                 ldb_dn_get_linearized(msg->dn),
377                                                 *error_string);
378                 TALLOC_FREE(tmp_ctx);
379                 return status;
380         }
381
382         managed_pw_utf8 = cli_credentials_get_password(cred);
383
384         previous_managed_pw_utf8 = cli_credentials_get_old_password(cred);
385
386         salt_principal = cli_credentials_get_salt_principal(cred, tmp_ctx);
387         if (salt_principal == NULL) {
388                 *error_string = talloc_asprintf(mem_ctx,
389                                                 "Failed to generate salt principal for %s",
390                                                 ldb_dn_get_linearized(msg->dn));
391                 TALLOC_FREE(tmp_ctx);
392                 return NT_STATUS_NO_MEMORY;
393         }
394
395         ret = smb_krb5_fill_keytab(tmp_ctx,
396                                    salt_principal,
397                                    kvno,
398                                    managed_pw_utf8,
399                                    previous_managed_pw_utf8,
400                                    supported_enctypes,
401                                    1,
402                                    &principal,
403                                    context,
404                                    keytab,
405                                    true,
406                                    error_string);
407         if (ret) {
408                 *error_string = talloc_asprintf(mem_ctx,
409                                                 "Failed to add keys from %s to keytab: %s",
410                                                 ldb_dn_get_linearized(msg->dn),
411                                                 *error_string);
412                 TALLOC_FREE(tmp_ctx);
413                 return NT_STATUS_UNSUCCESSFUL;
414         }
415
416         TALLOC_FREE(tmp_ctx);
417         return NT_STATUS_OK;
418 }
419
420 /**
421  * @brief Update a Kerberos keytab and removes any obsolete keytab entries.
422  *
423  * If the keytab does not exist, this function will create one.
424  *
425  * @param[in] parent_ctx        Talloc memory context
426  * @param[in] context           Kerberos context
427  * @param[in] keytab_name       Keytab to open
428  * @param[in] samAccountName    User account to update
429  * @param[in] realm             Kerberos realm
430  * @param[in] SPNs              Service principal names to update
431  * @param[in] num_SPNs          Length of SPNs
432  * @param[in] saltPrincipal     Salt used for AES encryption.
433  *                              Required, unless delete_all_kvno is set.
434  * @param[in] old_secret        Old password
435  * @param[in] new_secret        New password
436  * @param[in] kvno              Current key version number
437  * @param[in] supp_enctypes     msDS-SupportedEncryptionTypes bit-field
438  * @param[in] delete_all_kvno   Removes all obsolete entries, without
439  *                              recreating the keytab.
440  * @param[out] _keytab          If supplied, returns the keytab
441  * @param[out] perror_string    Error string on failure
442  *
443  * @return                      0 on success, errno on failure
444  */
445 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
446                                 krb5_context context,
447                                 const char *keytab_name,
448                                 const char *samAccountName,
449                                 const char *realm,
450                                 const char **SPNs,
451                                 int num_SPNs,
452                                 const char *saltPrincipal,
453                                 const char *new_secret,
454                                 const char *old_secret,
455                                 int kvno,
456                                 uint32_t supp_enctypes,
457                                 bool delete_all_kvno,
458                                 krb5_keytab *_keytab,
459                                 const char **perror_string)
460 {
461         krb5_keytab keytab = NULL;
462         krb5_error_code ret;
463         bool found_previous = false;
464         TALLOC_CTX *tmp_ctx = NULL;
465         krb5_principal *principals = NULL;
466         uint32_t num_principals = 0;
467         char *upper_realm;
468         const char *error_string = NULL;
469
470         if (keytab_name == NULL) {
471                 return ENOENT;
472         }
473
474         ret = krb5_kt_resolve(context, keytab_name, &keytab);
475         if (ret) {
476                 *perror_string = smb_get_krb5_error_message(context,
477                                                            ret, parent_ctx);
478                 return ret;
479         }
480
481         DEBUG(5, ("Opened keytab %s\n", keytab_name));
482
483         tmp_ctx = talloc_new(parent_ctx);
484         if (!tmp_ctx) {
485                 *perror_string = talloc_strdup(parent_ctx,
486                                               "Failed to allocate memory context");
487                 ret = ENOMEM;
488                 goto done;
489         }
490
491         upper_realm = strupper_talloc(tmp_ctx, realm);
492         if (upper_realm == NULL) {
493                 *perror_string = talloc_strdup(parent_ctx,
494                                               "Cannot allocate memory to upper case realm");
495                 ret = ENOMEM;
496                 goto done;
497         }
498
499         ret = smb_krb5_create_principals_array(tmp_ctx,
500                                                context,
501                                                samAccountName,
502                                                upper_realm,
503                                                num_SPNs,
504                                                SPNs,
505                                                &num_principals,
506                                                &principals,
507                                                &error_string);
508         if (ret != 0) {
509                 *perror_string = talloc_asprintf(parent_ctx,
510                         "Failed to load principals from ldb message: %s\n",
511                         error_string);
512                 goto done;
513         }
514
515         ret = smb_krb5_remove_obsolete_keytab_entries(tmp_ctx,
516                                                       context,
517                                                       keytab,
518                                                       num_principals,
519                                                       principals,
520                                                       kvno,
521                                                       &found_previous,
522                                                       &error_string);
523         if (ret != 0) {
524                 *perror_string = talloc_asprintf(parent_ctx,
525                         "Failed to remove old principals from keytab: %s\n",
526                         error_string);
527                 goto done;
528         }
529
530         if (!delete_all_kvno) {
531                 /* Create a new keytab.  If during the cleanout we found
532                  * entries for kvno -1, then don't try and duplicate them.
533                  * Otherwise, add kvno, and kvno -1 */
534                 if (saltPrincipal == NULL) {
535                         *perror_string = talloc_strdup(parent_ctx,
536                                                        "No saltPrincipal provided");
537                         ret = EINVAL;
538                         goto done;
539                 }
540
541                 ret = smb_krb5_fill_keytab(tmp_ctx,
542                                     saltPrincipal,
543                                     kvno, new_secret, old_secret,
544                                     supp_enctypes,
545                                     num_principals,
546                                     principals,
547                                     context, keytab,
548                                     found_previous ? false : true,
549                                     &error_string);
550                 if (ret) {
551                         *perror_string = talloc_steal(parent_ctx, error_string);
552                 }
553         }
554
555         if (ret == 0 && _keytab != NULL) {
556                 /* caller wants the keytab handle back */
557                 *_keytab = keytab;
558         }
559
560 done:
561         keytab_principals_free(context, num_principals, principals);
562         if (ret != 0 || _keytab == NULL) {
563                 krb5_kt_close(context, keytab);
564         }
565         talloc_free(tmp_ctx);
566         return ret;
567 }
568
569 /**
570  * @brief Wrapper around smb_krb5_update_keytab() for creating an in-memory keytab
571  *
572  * @param[in] parent_ctx        Talloc memory context
573  * @param[in] context           Kerberos context
574  * @param[in] new_secret        New password
575  * @param[in] samAccountName    User account to update
576  * @param[in] realm             Kerberos realm
577  * @param[in] salt_principal    Salt used for AES encryption.
578  *                              Required, unless delete_all_kvno is set.
579  * @param[in] kvno              Current key version number
580  * @param[out] keytab           If supplied, returns the keytab
581  * @param[out] keytab_name      Returns the created keytab name
582  *
583  * @return                      0 on success, errno on failure
584  */
585 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
586                                 krb5_context context,
587                                 const char *new_secret,
588                                 const char *samAccountName,
589                                 const char *realm,
590                                 const char *salt_principal,
591                                 int kvno,
592                                 krb5_keytab *keytab,
593                                 const char **keytab_name)
594 {
595         krb5_error_code ret;
596         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
597         const char *rand_string;
598         const char *error_string = NULL;
599         if (!mem_ctx) {
600                 return ENOMEM;
601         }
602
603         rand_string = generate_random_str(mem_ctx, 16);
604         if (!rand_string) {
605                 talloc_free(mem_ctx);
606                 return ENOMEM;
607         }
608
609         *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
610         if (*keytab_name == NULL) {
611                 talloc_free(mem_ctx);
612                 return ENOMEM;
613         }
614
615         ret = smb_krb5_update_keytab(mem_ctx, context,
616                                      *keytab_name, samAccountName, realm,
617                                      NULL, 0, salt_principal, new_secret, NULL,
618                                      kvno, ENC_ALL_TYPES,
619                                      false, keytab, &error_string);
620         if (ret == 0) {
621                 talloc_steal(parent_ctx, *keytab_name);
622         } else {
623                 DEBUG(0, ("Failed to create in-memory keytab: %s\n",
624                           error_string));
625                 *keytab_name = NULL;
626         }
627         talloc_free(mem_ctx);
628         return ret;
629 }