Line data Source code
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/samba_kdc.h"
31 : #include "kdc/mit_samba.h"
32 : #include "kdb_samba.h"
33 :
34 : #undef DBGC_CLASS
35 : #define DBGC_CLASS DBGC_KERBEROS
36 :
37 : #define ADMIN_LIFETIME 60*60*3 /* 3 hours */
38 :
39 92 : krb5_error_code ks_get_principal(krb5_context context,
40 : krb5_const_principal principal,
41 : unsigned int kflags,
42 : krb5_db_entry **kentry)
43 : {
44 : struct mit_samba_context *mit_ctx;
45 : krb5_error_code code;
46 :
47 92 : mit_ctx = ks_get_context(context);
48 92 : if (mit_ctx == NULL) {
49 0 : return KRB5_KDB_DBNOTINITED;
50 : }
51 :
52 92 : code = mit_samba_get_principal(mit_ctx,
53 : principal,
54 : kflags,
55 : kentry);
56 92 : if (code != 0) {
57 0 : goto cleanup;
58 : }
59 :
60 92 : cleanup:
61 :
62 92 : return code;
63 : }
64 :
65 0 : static void ks_free_principal_e_data(krb5_context context, krb5_octet *e_data)
66 : {
67 : struct samba_kdc_entry *skdc_entry;
68 :
69 0 : skdc_entry = talloc_get_type_abort(e_data,
70 : struct samba_kdc_entry);
71 0 : skdc_entry->kdc_entry = NULL;
72 0 : TALLOC_FREE(skdc_entry);
73 0 : }
74 :
75 0 : void ks_free_principal(krb5_context context, krb5_db_entry *entry)
76 : {
77 0 : krb5_tl_data *tl_data_next = NULL;
78 0 : krb5_tl_data *tl_data = NULL;
79 : size_t i, j;
80 :
81 0 : if (entry != NULL) {
82 0 : krb5_free_principal(context, entry->princ);
83 :
84 0 : for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
85 0 : tl_data_next = tl_data->tl_data_next;
86 0 : if (tl_data->tl_data_contents != NULL) {
87 0 : free(tl_data->tl_data_contents);
88 : }
89 0 : free(tl_data);
90 : }
91 :
92 0 : if (entry->key_data != NULL) {
93 0 : for (i = 0; i < entry->n_key_data; i++) {
94 0 : for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
95 0 : if (entry->key_data[i].key_data_length[j] != 0) {
96 0 : if (entry->key_data[i].key_data_contents[j] != NULL) {
97 0 : memset(entry->key_data[i].key_data_contents[j], 0, entry->key_data[i].key_data_length[j]);
98 0 : free(entry->key_data[i].key_data_contents[j]);
99 : }
100 : }
101 0 : entry->key_data[i].key_data_contents[j] = NULL;
102 0 : entry->key_data[i].key_data_length[j] = 0;
103 0 : entry->key_data[i].key_data_type[j] = 0;
104 : }
105 : }
106 0 : free(entry->key_data);
107 : }
108 :
109 0 : if (entry->e_data) {
110 0 : ks_free_principal_e_data(context, entry->e_data);
111 : }
112 :
113 0 : free(entry);
114 : }
115 0 : }
116 :
117 92 : static krb5_boolean ks_is_master_key_principal(krb5_context context,
118 : krb5_const_principal princ)
119 : {
120 92 : return krb5_princ_size(context, princ) == 2 &&
121 184 : ks_data_eq_string(princ->data[0], "K") &&
122 0 : ks_data_eq_string(princ->data[1], "M");
123 : }
124 :
125 0 : static krb5_error_code ks_get_master_key_principal(krb5_context context,
126 : krb5_const_principal princ,
127 : krb5_db_entry **kentry_ptr)
128 : {
129 : krb5_error_code code;
130 : krb5_key_data *key_data;
131 : krb5_timestamp now;
132 : krb5_db_entry *kentry;
133 :
134 0 : *kentry_ptr = NULL;
135 :
136 0 : kentry = calloc(1, sizeof(krb5_db_entry));
137 0 : if (kentry == NULL) {
138 0 : return ENOMEM;
139 : }
140 :
141 0 : kentry->magic = KRB5_KDB_MAGIC_NUMBER;
142 0 : kentry->len = KRB5_KDB_V1_BASE_LENGTH;
143 0 : kentry->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
144 :
145 0 : if (princ == NULL) {
146 0 : code = krb5_parse_name(context, KRB5_KDB_M_NAME, &kentry->princ);
147 : } else {
148 0 : code = krb5_copy_principal(context, princ, &kentry->princ);
149 : }
150 0 : if (code != 0) {
151 0 : krb5_db_free_principal(context, kentry);
152 0 : return code;
153 : }
154 :
155 0 : now = time(NULL);
156 :
157 0 : code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
158 0 : if (code != 0) {
159 0 : krb5_db_free_principal(context, kentry);
160 0 : return code;
161 : }
162 :
163 : /* Return a dummy key */
164 0 : kentry->n_key_data = 1;
165 0 : kentry->key_data = calloc(1, sizeof(krb5_key_data));
166 0 : if (code != 0) {
167 0 : krb5_db_free_principal(context, kentry);
168 0 : return code;
169 : }
170 :
171 0 : key_data = &kentry->key_data[0];
172 :
173 0 : key_data->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
174 0 : key_data->key_data_kvno = 1;
175 0 : key_data->key_data_type[0] = ENCTYPE_UNKNOWN;
176 0 : if (code != 0) {
177 0 : krb5_db_free_principal(context, kentry);
178 0 : return code;
179 : }
180 :
181 0 : *kentry_ptr = kentry;
182 :
183 0 : return 0;
184 : }
185 :
186 0 : static krb5_error_code ks_create_principal(krb5_context context,
187 : krb5_const_principal princ,
188 : int attributes,
189 : int max_life,
190 : const char *password,
191 : krb5_db_entry **kentry_ptr)
192 : {
193 : krb5_error_code code;
194 : krb5_key_data *key_data;
195 : krb5_timestamp now;
196 : krb5_db_entry *kentry;
197 : krb5_keyblock key;
198 : krb5_data salt;
199 : krb5_data pwd;
200 0 : int enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
201 0 : int sts = KRB5_KDB_SALTTYPE_SPECIAL;
202 :
203 0 : if (princ == NULL) {
204 0 : return KRB5_KDB_NOENTRY;
205 : }
206 :
207 0 : *kentry_ptr = NULL;
208 :
209 0 : kentry = calloc(1, sizeof(krb5_db_entry));
210 0 : if (kentry == NULL) {
211 0 : return ENOMEM;
212 : }
213 :
214 0 : kentry->magic = KRB5_KDB_MAGIC_NUMBER;
215 0 : kentry->len = KRB5_KDB_V1_BASE_LENGTH;
216 :
217 0 : if (attributes > 0) {
218 0 : kentry->attributes = attributes;
219 : }
220 :
221 0 : if (max_life > 0) {
222 0 : kentry->max_life = max_life;
223 : }
224 :
225 0 : code = krb5_copy_principal(context, princ, &kentry->princ);
226 0 : if (code != 0) {
227 0 : krb5_db_free_principal(context, kentry);
228 0 : return code;
229 : }
230 :
231 0 : now = time(NULL);
232 :
233 0 : code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
234 0 : if (code != 0) {
235 0 : krb5_db_free_principal(context, kentry);
236 0 : return code;
237 : }
238 :
239 0 : code = mit_samba_generate_salt(&salt);
240 0 : if (code != 0) {
241 0 : krb5_db_free_principal(context, kentry);
242 0 : return code;
243 : }
244 :
245 0 : if (password != NULL) {
246 0 : pwd.data = strdup(password);
247 0 : pwd.length = strlen(password);
248 : } else {
249 : /* create a random password */
250 0 : code = mit_samba_generate_random_password(&pwd);
251 0 : if (code != 0) {
252 0 : krb5_db_free_principal(context, kentry);
253 0 : return code;
254 : }
255 : }
256 :
257 0 : code = krb5_c_string_to_key(context, enctype, &pwd, &salt, &key);
258 0 : SAFE_FREE(pwd.data);
259 0 : if (code != 0) {
260 0 : krb5_db_free_principal(context, kentry);
261 0 : return code;
262 : }
263 :
264 0 : kentry->n_key_data = 1;
265 0 : kentry->key_data = calloc(1, sizeof(krb5_key_data));
266 0 : if (code != 0) {
267 0 : krb5_db_free_principal(context, kentry);
268 0 : return code;
269 : }
270 :
271 0 : key_data = &kentry->key_data[0];
272 :
273 0 : key_data->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
274 0 : key_data->key_data_kvno = 1;
275 0 : key_data->key_data_type[0] = key.enctype;
276 0 : key_data->key_data_length[0] = key.length;
277 0 : key_data->key_data_contents[0] = key.contents;
278 0 : key_data->key_data_type[1] = sts;
279 0 : key_data->key_data_length[1] = salt.length;
280 0 : key_data->key_data_contents[1] = (krb5_octet*)salt.data;
281 :
282 0 : *kentry_ptr = kentry;
283 :
284 0 : return 0;
285 : }
286 :
287 0 : static krb5_error_code ks_get_admin_principal(krb5_context context,
288 : krb5_const_principal princ,
289 : krb5_db_entry **kentry_ptr)
290 : {
291 0 : krb5_error_code code = EINVAL;
292 :
293 0 : code = ks_create_principal(context,
294 : princ,
295 : KRB5_KDB_DISALLOW_TGT_BASED,
296 : ADMIN_LIFETIME,
297 : NULL,
298 : kentry_ptr);
299 :
300 0 : return code;
301 : }
302 :
303 92 : krb5_error_code kdb_samba_db_get_principal(krb5_context context,
304 : krb5_const_principal princ,
305 : unsigned int kflags,
306 : krb5_db_entry **kentry)
307 : {
308 : struct mit_samba_context *mit_ctx;
309 : krb5_error_code code;
310 :
311 92 : mit_ctx = ks_get_context(context);
312 92 : if (mit_ctx == NULL) {
313 0 : return KRB5_KDB_DBNOTINITED;
314 : }
315 :
316 92 : if (ks_is_master_key_principal(context, princ)) {
317 0 : return ks_get_master_key_principal(context, princ, kentry);
318 : }
319 :
320 : /*
321 : * Fake a kadmin/admin and kadmin/history principal so that kadmindd can
322 : * start
323 : */
324 184 : if (ks_is_kadmin_admin(context, princ) ||
325 92 : ks_is_kadmin_history(context, princ)) {
326 0 : return ks_get_admin_principal(context, princ, kentry);
327 : }
328 :
329 92 : code = ks_get_principal(context, princ, kflags, kentry);
330 :
331 : /*
332 : * This restricts the changepw account so it isn't able to request a
333 : * service ticket. It also marks the principal as the changepw service.
334 : */
335 92 : if (ks_is_kadmin_changepw(context, princ)) {
336 : /* FIXME: shouldn't we also set KRB5_KDB_DISALLOW_TGT_BASED ?
337 : * testing showed that setpw kpasswd command fails then on the
338 : * server though... */
339 92 : (*kentry)->attributes |= KRB5_KDB_PWCHANGE_SERVICE;
340 92 : (*kentry)->max_life = CHANGEPW_LIFETIME;
341 : }
342 :
343 92 : return code;
344 : }
345 :
346 0 : krb5_error_code kdb_samba_db_put_principal(krb5_context context,
347 : krb5_db_entry *entry,
348 : char **db_args)
349 : {
350 :
351 : /* NOTE: deferred, samba does not allow the KDC to store
352 : * principals for now. We should not return KRB5_KDB_DB_INUSE as this
353 : * would result in confusing error messages after password changes. */
354 0 : return 0;
355 : }
356 :
357 0 : krb5_error_code kdb_samba_db_delete_principal(krb5_context context,
358 : krb5_const_principal princ)
359 : {
360 :
361 : /* NOTE: deferred, samba does not allow the KDC to delete
362 : * principals for now */
363 0 : return KRB5_KDB_DB_INUSE;
364 : }
365 :
366 0 : krb5_error_code kdb_samba_db_iterate(krb5_context context,
367 : char *match_entry,
368 : int (*func)(krb5_pointer, krb5_db_entry *),
369 : krb5_pointer func_arg,
370 : krb5_flags iterflags)
371 : {
372 : struct mit_samba_context *mit_ctx;
373 0 : krb5_db_entry *kentry = NULL;
374 : krb5_error_code code;
375 :
376 :
377 0 : mit_ctx = ks_get_context(context);
378 0 : if (mit_ctx == NULL) {
379 0 : return KRB5_KDB_DBNOTINITED;
380 : }
381 :
382 0 : code = mit_samba_get_firstkey(mit_ctx, &kentry);
383 0 : while (code == 0) {
384 0 : code = (*func)(func_arg, kentry);
385 0 : if (code != 0) {
386 0 : break;
387 : }
388 :
389 0 : code = mit_samba_get_nextkey(mit_ctx, &kentry);
390 : }
391 :
392 0 : if (code == KRB5_KDB_NOENTRY) {
393 0 : code = 0;
394 : }
395 :
396 0 : return code;
397 : }
|