mit-kdb: support MIT Kerberos 1.16 KDB API changes
[samba.git] / source4 / kdc / mit-kdb / kdb_samba_principals.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Samba KDB plugin for MIT Kerberos
5
6    Copyright (c) 2010      Simo Sorce <idra@samba.org>.
7    Copyright (c) 2014      Andreas Schneider <asn@samba.org>
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
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 #include "includes.h"
24
25 #include "system/kerberos.h"
26
27 #include <profile.h>
28 #include <kdb.h>
29
30 #include "kdc/mit_samba.h"
31 #include "kdb_samba.h"
32
33 #define ADMIN_LIFETIME 60*60*3 /* 3 hours */
34 #define CHANGEPW_LIFETIME 60*5 /* 5 minutes */
35
36 static krb5_error_code ks_get_principal(krb5_context context,
37                                         krb5_const_principal principal,
38                                         unsigned int kflags,
39                                         krb5_db_entry **kentry)
40 {
41         struct mit_samba_context *mit_ctx;
42         krb5_error_code code;
43
44         mit_ctx = ks_get_context(context);
45         if (mit_ctx == NULL) {
46                 return KRB5_KDB_DBNOTINITED;
47         }
48
49         code = mit_samba_get_principal(mit_ctx,
50                                        principal,
51                                        kflags,
52                                        kentry);
53         if (code != 0) {
54                 goto cleanup;
55         }
56
57 cleanup:
58
59         return code;
60 }
61
62 static krb5_boolean ks_is_master_key_principal(krb5_context context,
63                                                krb5_const_principal princ)
64 {
65         return krb5_princ_size(context, princ) == 2 &&
66                ks_data_eq_string(princ->data[0], "K") &&
67                ks_data_eq_string(princ->data[1], "M");
68 }
69
70 static krb5_error_code ks_get_master_key_principal(krb5_context context,
71                                                    krb5_const_principal princ,
72                                                    krb5_db_entry **kentry_ptr)
73 {
74         krb5_error_code code;
75         krb5_key_data *key_data;
76         krb5_timestamp now;
77         krb5_db_entry *kentry;
78
79         *kentry_ptr = NULL;
80
81         kentry = calloc(1, sizeof(krb5_db_entry));
82         if (kentry == NULL) {
83                 return ENOMEM;
84         }
85
86         kentry->magic = KRB5_KDB_MAGIC_NUMBER;
87         kentry->len = KRB5_KDB_V1_BASE_LENGTH;
88         kentry->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
89
90         if (princ == NULL) {
91                 code = krb5_parse_name(context, KRB5_KDB_M_NAME, &kentry->princ);
92         } else {
93                 code = krb5_copy_principal(context, princ, &kentry->princ);
94         }
95         if (code != 0) {
96                 krb5_db_free_principal(context, kentry);
97                 return code;
98         }
99
100         now = time(NULL);
101
102         code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
103         if (code != 0) {
104                 krb5_db_free_principal(context, kentry);
105                 return code;
106         }
107
108         /* Return a dummy key */
109         kentry->n_key_data = 1;
110         kentry->key_data = calloc(1, sizeof(krb5_key_data));
111         if (code != 0) {
112                 krb5_db_free_principal(context, kentry);
113                 return code;
114         }
115
116         key_data = &kentry->key_data[0];
117
118         key_data->key_data_ver          = KRB5_KDB_V1_KEY_DATA_ARRAY;
119         key_data->key_data_kvno         = 1;
120         key_data->key_data_type[0]      = ENCTYPE_UNKNOWN;
121         if (code != 0) {
122                 krb5_db_free_principal(context, kentry);
123                 return code;
124         }
125
126         *kentry_ptr = kentry;
127
128         return 0;
129 }
130
131 static krb5_error_code ks_create_principal(krb5_context context,
132                                            krb5_const_principal princ,
133                                            int attributes,
134                                            int max_life,
135                                            const char *password,
136                                            krb5_db_entry **kentry_ptr)
137 {
138         krb5_error_code code;
139         krb5_key_data *key_data;
140         krb5_timestamp now;
141         krb5_db_entry *kentry;
142         krb5_keyblock key;
143         krb5_data salt;
144         krb5_data pwd;
145         int enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
146         int sts = KRB5_KDB_SALTTYPE_SPECIAL;
147
148         if (princ == NULL) {
149                 return KRB5_KDB_NOENTRY;
150         }
151
152         *kentry_ptr = NULL;
153
154         kentry = calloc(1, sizeof(krb5_db_entry));
155         if (kentry == NULL) {
156                 return ENOMEM;
157         }
158
159         kentry->magic = KRB5_KDB_MAGIC_NUMBER;
160         kentry->len = KRB5_KDB_V1_BASE_LENGTH;
161
162         if (attributes > 0) {
163                 kentry->attributes = attributes;
164         }
165
166         if (max_life > 0) {
167                 kentry->max_life = max_life;
168         }
169
170         code = krb5_copy_principal(context, princ, &kentry->princ);
171         if (code != 0) {
172                 krb5_db_free_principal(context, kentry);
173                 return code;
174         }
175
176         now = time(NULL);
177
178         code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
179         if (code != 0) {
180                 krb5_db_free_principal(context, kentry);
181                 return code;
182         }
183
184         code = mit_samba_generate_salt(&salt);
185         if (code != 0) {
186                 krb5_db_free_principal(context, kentry);
187                 return code;
188         }
189
190         if (password != NULL) {
191                 pwd.data = strdup(password);
192                 pwd.length = strlen(password);
193         } else {
194                 /* create a random password */
195                 code = mit_samba_generate_random_password(&pwd);
196                 if (code != 0) {
197                         krb5_db_free_principal(context, kentry);
198                         return code;
199                 }
200         }
201
202         code = krb5_c_string_to_key(context, enctype, &pwd, &salt, &key);
203         SAFE_FREE(pwd.data);
204         if (code != 0) {
205                 krb5_db_free_principal(context, kentry);
206                 return code;
207         }
208
209         kentry->n_key_data = 1;
210         kentry->key_data = calloc(1, sizeof(krb5_key_data));
211         if (code != 0) {
212                 krb5_db_free_principal(context, kentry);
213                 return code;
214         }
215
216         key_data = &kentry->key_data[0];
217
218         key_data->key_data_ver          = KRB5_KDB_V1_KEY_DATA_ARRAY;
219         key_data->key_data_kvno         = 1;
220         key_data->key_data_type[0]      = key.enctype;
221         key_data->key_data_length[0]    = key.length;
222         key_data->key_data_contents[0]  = key.contents;
223         key_data->key_data_type[1]      = sts;
224         key_data->key_data_length[1]    = salt.length;
225         key_data->key_data_contents[1]  = (krb5_octet*)salt.data;
226
227         *kentry_ptr = kentry;
228
229         return 0;
230 }
231
232 static krb5_error_code ks_get_admin_principal(krb5_context context,
233                                               krb5_const_principal princ,
234                                               krb5_db_entry **kentry_ptr)
235 {
236         krb5_error_code code = EINVAL;
237
238         code = ks_create_principal(context,
239                                    princ,
240                                    KRB5_KDB_DISALLOW_TGT_BASED,
241                                    ADMIN_LIFETIME,
242                                    NULL,
243                                    kentry_ptr);
244
245         return code;
246 }
247
248 krb5_error_code kdb_samba_db_get_principal(krb5_context context,
249                                            krb5_const_principal princ,
250                                            unsigned int kflags,
251                                            krb5_db_entry **kentry)
252 {
253         struct mit_samba_context *mit_ctx;
254         krb5_error_code code;
255
256         mit_ctx = ks_get_context(context);
257         if (mit_ctx == NULL) {
258                 return KRB5_KDB_DBNOTINITED;
259         }
260
261         if (ks_is_master_key_principal(context, princ)) {
262                 return ks_get_master_key_principal(context, princ, kentry);
263         }
264
265         /*
266          * Fake a kadmin/admin and kadmin/history principal so that kadmindd can
267          * start
268          */
269         if (ks_is_kadmin_admin(context, princ) ||
270             ks_is_kadmin_history(context, princ)) {
271                 return ks_get_admin_principal(context, princ, kentry);
272         }
273
274         code = ks_get_principal(context, princ, kflags, kentry);
275
276         /*
277          * This restricts the changepw account so it isn't able to request a
278          * service ticket. It also marks the principal as the changepw service.
279          */
280         if (ks_is_kadmin_changepw(context, princ)) {
281                 /* FIXME: shouldn't we also set KRB5_KDB_DISALLOW_TGT_BASED ?
282                  * testing showed that setpw kpasswd command fails then on the
283                  * server though... */
284                 (*kentry)->attributes |= KRB5_KDB_PWCHANGE_SERVICE;
285                 (*kentry)->max_life = CHANGEPW_LIFETIME;
286         }
287
288         return code;
289 }
290
291 krb5_error_code kdb_samba_db_put_principal(krb5_context context,
292                                            krb5_db_entry *entry,
293                                            char **db_args)
294 {
295
296         /* NOTE: deferred, samba does not allow the KDC to store
297          * principals for now. We should not return KRB5_KDB_DB_INUSE as this
298          * would result in confusing error messages after password changes. */
299         return 0;
300 }
301
302 krb5_error_code kdb_samba_db_delete_principal(krb5_context context,
303                                               krb5_const_principal princ)
304 {
305
306         /* NOTE: deferred, samba does not allow the KDC to delete
307          * principals for now */
308         return KRB5_KDB_DB_INUSE;
309 }
310
311 #if KRB5_KDB_API_VERSION >= 8
312 krb5_error_code kdb_samba_db_iterate(krb5_context context,
313                                      char *match_entry,
314                                      int (*func)(krb5_pointer, krb5_db_entry *),
315                                      krb5_pointer func_arg,
316                                      krb5_flags iterflags)
317 #else
318 krb5_error_code kdb_samba_db_iterate(krb5_context context,
319                                      char *match_entry,
320                                      int (*func)(krb5_pointer, krb5_db_entry *),
321                                      krb5_pointer func_arg)
322 #endif
323 {
324         struct mit_samba_context *mit_ctx;
325         krb5_db_entry *kentry = NULL;
326         krb5_error_code code;
327
328
329         mit_ctx = ks_get_context(context);
330         if (mit_ctx == NULL) {
331                 return KRB5_KDB_DBNOTINITED;
332         }
333
334         code = mit_samba_get_firstkey(mit_ctx, &kentry);
335         while (code == 0) {
336                 code = (*func)(func_arg, kentry);
337                 if (code != 0) {
338                         break;
339                 }
340
341                 code = mit_samba_get_nextkey(mit_ctx, &kentry);
342         }
343
344         if (code == KRB5_KDB_NOENTRY) {
345                 code = 0;
346         }
347
348         return code;
349 }