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