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