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