1 /*
2 * lib/kdb/kdb_ldap/ldap_principal.c
3 *
4 * Copyright (c) 2004-2005, Novell, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * The copyright holder's name is not used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30 /*
31 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
32 * Use is subject to license terms.
33 */
34
35 #include "ldap_main.h"
36 #include "kdb_ldap.h"
37 #include "ldap_principal.h"
38 #include "princ_xdr.h"
39 #include "ldap_err.h"
40 #include <libintl.h>
41
42 struct timeval timelimit = {300, 0}; /* 5 minutes */
43 char *principal_attributes[] = { "krbprincipalname",
44 "objectclass",
45 "krbprincipalkey",
46 "krbmaxrenewableage",
47 "krbmaxticketlife",
48 "krbticketflags",
49 "krbprincipalexpiration",
50 "krbticketpolicyreference",
51 "krbUpEnabled",
52 "krbpwdpolicyreference",
53 "krbpasswordexpiration",
54 "krbLastFailedAuth",
55 "krbLoginFailedCount",
56 "krbLastSuccessfulAuth",
57 #ifdef HAVE_EDIRECTORY
58 "loginexpirationtime",
59 "logindisabled",
60 #endif
61 "loginexpirationtime",
62 "logindisabled",
63 "modifytimestamp",
64 "krbLastPwdChange",
65 "krbExtraData",
66 "krbObjectReferences",
67 NULL };
68
69 /* Must match KDB_*_ATTR macros in ldap_principal.h. */
70 static char *attributes_set[] = { "krbmaxticketlife",
71 "krbmaxrenewableage",
72 "krbticketflags",
73 "krbprincipalexpiration",
74 "krbticketpolicyreference",
75 "krbUpEnabled",
76 "krbpwdpolicyreference",
77 "krbpasswordexpiration",
78 "krbprincipalkey",
79 "krblastpwdchange",
80 "krbextradata",
81 "krbLastSuccessfulAuth",
82 "krbLastFailedAuth",
83 "krbLoginFailedCount",
84 NULL };
85
86 void
krb5_dbe_free_contents(context,entry)87 krb5_dbe_free_contents(context, entry)
88 krb5_context context;
89 krb5_db_entry *entry;
90 {
91 krb5_tl_data *tl_data_next=NULL;
92 krb5_tl_data *tl_data=NULL;
93 int i, j;
94
95 if (entry->e_data)
96 free(entry->e_data);
97 if (entry->princ)
98 krb5_free_principal(context, entry->princ);
99 for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
100 tl_data_next = tl_data->tl_data_next;
101 if (tl_data->tl_data_contents)
102 free(tl_data->tl_data_contents);
103 free(tl_data);
104 }
105 if (entry->key_data) {
106 for (i = 0; i < entry->n_key_data; i++) {
107 for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
108 if (entry->key_data[i].key_data_length[j]) {
109 if (entry->key_data[i].key_data_contents[j]) {
110 memset(entry->key_data[i].key_data_contents[j],
111 0,
112 (unsigned) entry->key_data[i].key_data_length[j]);
113 free (entry->key_data[i].key_data_contents[j]);
114 }
115 }
116 entry->key_data[i].key_data_contents[j] = NULL;
117 entry->key_data[i].key_data_length[j] = 0;
118 entry->key_data[i].key_data_type[j] = 0;
119 }
120 }
121 free(entry->key_data);
122 }
123 memset(entry, 0, sizeof(*entry));
124 return;
125 }
126
127
128 krb5_error_code
krb5_ldap_free_principal(kcontext,entries,nentries)129 krb5_ldap_free_principal(kcontext , entries, nentries)
130 krb5_context kcontext;
131 krb5_db_entry *entries;
132 int nentries;
133 {
134 register int i;
135 for (i = 0; i < nentries; i++)
136 krb5_dbe_free_contents(kcontext, &entries[i]);
137 return 0;
138 }
139
140 krb5_error_code
krb5_ldap_iterate(context,match_expr,func,func_arg,db_args)141 krb5_ldap_iterate(context, match_expr, func, func_arg, db_args)
142 krb5_context context;
143 char *match_expr;
144 krb5_error_code (*func) (krb5_pointer, krb5_db_entry *);
145 krb5_pointer func_arg;
146 /* Solaris Kerberos: adding support for -rev/recurse flags */
147 char **db_args;
148 {
149 krb5_db_entry entry;
150 krb5_principal principal;
151 char **subtree=NULL, *princ_name=NULL, *realm=NULL, **values=NULL, *filter=NULL;
152 unsigned int filterlen=0, tree=0, ntree=1, i=0;
153 krb5_error_code st=0, tempst=0;
154 LDAP *ld=NULL;
155 LDAPMessage *result=NULL, *ent=NULL;
156 kdb5_dal_handle *dal_handle=NULL;
157 krb5_ldap_context *ldap_context=NULL;
158 krb5_ldap_server_handle *ldap_server_handle=NULL;
159 char *default_match_expr = "*";
160
161 /* Clear the global error string */
162 krb5_clear_error_message(context);
163
164 /* Solaris Kerberos: adding support for -rev/recurse flags */
165 if (db_args) {
166 /* LDAP does not support db_args DB arguments for krb5_ldap_iterate */
167 krb5_set_error_message(context, EINVAL,
168 gettext("Unsupported argument \"%s\" for ldap"),
169 db_args[0]);
170 return EINVAL;
171 }
172
173 memset(&entry, 0, sizeof(krb5_db_entry));
174 SETUP_CONTEXT();
175
176 realm = ldap_context->lrparams->realm_name;
177 if (realm == NULL) {
178 realm = context->default_realm;
179 if (realm == NULL) {
180 st = EINVAL;
181 krb5_set_error_message(context, st, gettext("Default realm not set"));
182 goto cleanup;
183 }
184 }
185
186 /*
187 * If no match_expr then iterate through all krb princs like the db2 plugin
188 */
189 if (match_expr == NULL)
190 match_expr = default_match_expr;
191
192 filterlen = strlen(FILTER) + strlen(match_expr) + 2 + 1; /* 2 for closing brackets */
193 filter = malloc (filterlen);
194 CHECK_NULL(filter);
195 memset(filter, 0, filterlen);
196 /*LINTED*/
197 sprintf(filter, FILTER"%s))", match_expr);
198
199 if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntree)) != 0)
200 goto cleanup;
201
202 GET_HANDLE();
203
204 for (tree=0; tree < ntree; ++tree) {
205
206 LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes);
207 for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) {
208 if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
209 for (i=0; values[i] != NULL; ++i) {
210 if (values[i])
211 if (krb5_ldap_parse_principal_name(values[i], &princ_name) != 0)
212 continue;
213 if (krb5_parse_name(context, princ_name, &principal) != 0)
214 continue;
215 if (is_principal_in_realm(ldap_context, principal) == 0) {
216 if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, principal,
217 &entry)) != 0)
218 goto cleanup;
219 (*func)(func_arg, &entry);
220 krb5_dbe_free_contents(context, &entry);
221 (void) krb5_free_principal(context, principal);
222 if (princ_name)
223 free(princ_name);
224 break;
225 }
226 (void) krb5_free_principal(context, principal);
227 if (princ_name)
228 free(princ_name);
229 }
230 ldap_value_free(values);
231 }
232 } /* end of for (ent= ... */
233 ldap_msgfree(result);
234 } /* end of for (tree= ... */
235
236 cleanup:
237 if (filter)
238 free (filter);
239
240 for (;ntree; --ntree)
241 if (subtree[ntree-1])
242 free (subtree[ntree-1]);
243
244 /* Solaris Kerberos: fix memory leak */
245 if (subtree != NULL) {
246 free(subtree);
247 }
248 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
249 return st;
250 }
251
252
253 /*
254 * delete a principal from the directory.
255 */
256 krb5_error_code
krb5_ldap_delete_principal(context,searchfor,nentries)257 krb5_ldap_delete_principal(context, searchfor, nentries)
258 krb5_context context;
259 krb5_const_principal searchfor;
260 int *nentries; /* how many found & deleted */
261 {
262 char *user=NULL, *DN=NULL, *strval[10] = {NULL};
263 LDAPMod **mods=NULL;
264 LDAP *ld=NULL;
265 int j=0, ptype=0, pcount=0;
266 unsigned int attrsetmask=0;
267 krb5_error_code st=0;
268 krb5_boolean singleentry=FALSE;
269 KEY *secretkey=NULL;
270 kdb5_dal_handle *dal_handle=NULL;
271 krb5_ldap_context *ldap_context=NULL;
272 krb5_ldap_server_handle *ldap_server_handle=NULL;
273 krb5_db_entry entries;
274 krb5_boolean more=0;
275
276 /* Clear the global error string */
277 krb5_clear_error_message(context);
278
279 SETUP_CONTEXT();
280 /* get the principal info */
281 if ((st=krb5_ldap_get_principal(context, searchfor, &entries, nentries, &more)) != 0 || *nentries == 0)
282 goto cleanup;
283
284 if (((st=krb5_get_princ_type(context, &entries, &(ptype))) != 0) ||
285 ((st=krb5_get_attributes_mask(context, &entries, &(attrsetmask))) != 0) ||
286 ((st=krb5_get_princ_count(context, &entries, &(pcount))) != 0) ||
287 ((st=krb5_get_userdn(context, &entries, &(DN))) != 0))
288 goto cleanup;
289
290 if (DN == NULL) {
291 st = EINVAL;
292 krb5_set_error_message(context, st, gettext("DN information missing"));
293 goto cleanup;
294 }
295
296 GET_HANDLE();
297
298 if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT) {
299 st = ldap_delete_ext_s(ld, DN, NULL, NULL);
300 if (st != LDAP_SUCCESS) {
301 st = set_ldap_error (context, st, OP_DEL);
302 goto cleanup;
303 }
304 } else {
305 if (((st=krb5_unparse_name(context, searchfor, &user)) != 0)
306 || ((st=krb5_ldap_unparse_principal_name(user)) != 0))
307 goto cleanup;
308
309 memset(strval, 0, sizeof(strval));
310 strval[0] = user;
311 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_DELETE,
312 strval)) != 0)
313 goto cleanup;
314
315 singleentry = (pcount == 1) ? TRUE: FALSE;
316 if (singleentry == FALSE) {
317 if (secretkey != NULL) {
318 if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey", LDAP_MOD_DELETE | LDAP_MOD_BVALUES,
319 secretkey->keys)) != 0)
320 goto cleanup;
321 }
322 } else {
323 /*
324 * If the Kerberos user principal to be deleted happens to be the last one associated
325 * with the directory user object, then it is time to delete the other kerberos
326 * specific attributes like krbmaxticketlife, i.e, unkerberize the directory user.
327 * From the attrsetmask value, identify the attributes set on the directory user
328 * object and delete them.
329 * NOTE: krbsecretkey attribute has per principal entries. There can be chances that the
330 * other principals' keys are exisiting/left-over. So delete all the values.
331 */
332 while (attrsetmask) {
333 if (attrsetmask & 1) {
334 if ((st=krb5_add_str_mem_ldap_mod(&mods, attributes_set[j], LDAP_MOD_DELETE,
335 NULL)) != 0)
336 goto cleanup;
337 }
338 attrsetmask >>= 1;
339 ++j;
340 }
341
342 /* the same should be done with the objectclass attributes */
343 {
344 char *attrvalues[] = {"krbticketpolicyaux", "krbprincipalaux", NULL};
345 /* char *attrvalues[] = {"krbpwdpolicyrefaux", "krbticketpolicyaux", "krbprincipalaux", NULL}; */
346 int p, q, r=0, amask=0;
347
348 if ((st=checkattributevalue(ld, DN, "objectclass", attrvalues, &amask)) != 0)
349 goto cleanup;
350 memset(strval, 0, sizeof(strval));
351 for (p=1, q=0; p<=4; p<<=1, ++q)
352 if (p & amask)
353 strval[r++] = attrvalues[q];
354 strval[r] = NULL;
355 if (r > 0) {
356 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_DELETE,
357 strval)) != 0)
358 goto cleanup;
359 }
360 }
361 }
362 st=ldap_modify_ext_s(ld, DN, mods, NULL, NULL);
363 if (st != LDAP_SUCCESS) {
364 st = set_ldap_error(context, st, OP_MOD);
365 goto cleanup;
366 }
367 }
368
369 cleanup:
370 if (user)
371 free (user);
372
373 if (DN)
374 free (DN);
375
376 if (secretkey != NULL) {
377 int i=0;
378 while (i < secretkey->nkey) {
379 free (secretkey->keys[i]->bv_val);
380 free (secretkey->keys[i]);
381 ++i;
382 }
383 free (secretkey->keys);
384 free (secretkey);
385 }
386
387 if (st == 0)
388 krb5_ldap_free_principal(context, &entries, *nentries);
389
390 ldap_mods_free(mods, 1);
391 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
392 return st;
393 }
394
395
396 /*
397 * Function: krb5_ldap_unparse_principal_name
398 *
399 * Purpose: Removes '\\' that comes before every occurence of '@'
400 * in the principal name component.
401 *
402 * Arguments:
403 * user_name (input/output) Principal name
404 *
405 */
406
407 krb5_error_code
krb5_ldap_unparse_principal_name(char * user_name)408 krb5_ldap_unparse_principal_name(char *user_name)
409 {
410 char *tmp_princ_name=NULL, *princ_name=NULL, *tmp=NULL;
411 int l=0;
412 krb5_error_code st=0;
413
414 if (strstr(user_name, "\\@")) {
415
416 tmp_princ_name = strdup(user_name);
417 if (!tmp_princ_name) {
418 st = ENOMEM;
419 goto cleanup;
420 }
421 tmp = tmp_princ_name;
422
423 princ_name = (char *) malloc (strlen(user_name));
424 if (!princ_name) {
425 st = ENOMEM;
426 goto cleanup;
427 }
428 memset(princ_name, 0, strlen(user_name));
429
430 l = 0;
431 while (*tmp_princ_name) {
432 if ((*tmp_princ_name == '\\') && (*(tmp_princ_name+1) == '@')) {
433 tmp_princ_name += 1;
434 } else {
435 *(princ_name + l) = *tmp_princ_name++;
436 l++;
437 }
438 }
439
440 memset(user_name, 0, strlen(user_name));
441 /*LINTED*/
442 sprintf(user_name, "%s", princ_name);
443 }
444
445 cleanup:
446 if (tmp) {
447 free(tmp);
448 tmp = NULL;
449 }
450
451 if (princ_name) {
452 free(princ_name);
453 princ_name = NULL;
454 }
455
456 return st;
457 }
458
459
460 /*
461 * Function: krb5_ldap_parse_principal_name
462 *
463 * Purpose: Inserts '\\' before every occurence of '@'
464 * in the principal name component.
465 *
466 * Arguments:
467 * i_princ_name (input) Principal name without '\\'
468 * o_princ_name (output) Principal name with '\\'
469 *
470 * Note: The caller has to free the memory allocated for o_princ_name.
471 */
472
473 krb5_error_code
krb5_ldap_parse_principal_name(i_princ_name,o_princ_name)474 krb5_ldap_parse_principal_name(i_princ_name, o_princ_name)
475 char *i_princ_name;
476 char **o_princ_name;
477 {
478 char *tmp_princ_name = NULL, *princ_name = NULL, *at_rlm_name = NULL;
479 int l = 0, m = 0, tmp_princ_name_len = 0, princ_name_len = 0, at_count = 0;
480 krb5_error_code st = 0;
481
482 at_rlm_name = strrchr(i_princ_name, '@');
483
484 if (!at_rlm_name) {
485 *o_princ_name = strdup(i_princ_name);
486 if (!o_princ_name) {
487 st = ENOMEM;
488 goto cleanup;
489 }
490 } else {
491 tmp_princ_name_len = at_rlm_name - i_princ_name;
492
493 tmp_princ_name = (char *) malloc ((unsigned) tmp_princ_name_len + 1);
494 if (!tmp_princ_name) {
495 st = ENOMEM;
496 goto cleanup;
497 }
498 memset(tmp_princ_name, 0, (unsigned) tmp_princ_name_len + 1);
499 memcpy(tmp_princ_name, i_princ_name, (unsigned) tmp_princ_name_len);
500
501 l = 0;
502 while (tmp_princ_name[l]) {
503 if (tmp_princ_name[l++] == '@')
504 at_count++;
505 }
506
507 princ_name_len = strlen(i_princ_name) + at_count + 1;
508 princ_name = (char *) malloc ((unsigned) princ_name_len);
509 if (!princ_name) {
510 st = ENOMEM;
511 goto cleanup;
512 }
513 memset(princ_name, 0, (unsigned) princ_name_len);
514
515 l = 0;
516 m = 0;
517 while (tmp_princ_name[l]) {
518 if (tmp_princ_name[l] == '@') {
519 princ_name[m++]='\\';
520 }
521 princ_name[m++]=tmp_princ_name[l++];
522 }
523 /* Solaris Kerberos: using strlcat for safety */
524 strlcat(princ_name, at_rlm_name, princ_name_len);
525
526 *o_princ_name = princ_name;
527 }
528
529 cleanup:
530
531 if (tmp_princ_name) {
532 free(tmp_princ_name);
533 tmp_princ_name = NULL;
534 }
535
536 return st;
537 }
538