xref: /illumos-gate/usr/src/lib/krb5/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c (revision 54925bf60766fbb4f1f2d7c843721406a7b7a3fb)
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(context, match_expr, func, func_arg)
144     krb5_context           context;
145     char                   *match_expr;
146     krb5_error_code        (*func) (krb5_pointer, krb5_db_entry *);
147     krb5_pointer           func_arg;
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     memset(&entry, 0, sizeof(krb5_db_entry));
165     SETUP_CONTEXT();
166 
167     realm = ldap_context->lrparams->realm_name;
168     if (realm == NULL) {
169 	realm = context->default_realm;
170 	if (realm == NULL) {
171 	    st = EINVAL;
172 	    krb5_set_error_message(context, st, gettext("Default realm not set"));
173 	    goto cleanup;
174 	}
175     }
176 
177     /*
178      * If no match_expr then iterate through all krb princs like the db2 plugin
179      */
180     if (match_expr == NULL)
181 	match_expr = default_match_expr;
182 
183     filterlen = strlen(FILTER) + strlen(match_expr) + 2 + 1;  /* 2 for closing brackets */
184     filter = malloc (filterlen);
185     CHECK_NULL(filter);
186     memset(filter, 0, filterlen);
187     /*LINTED*/
188     sprintf(filter, FILTER"%s))", match_expr);
189 
190     if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntree)) != 0)
191 	goto cleanup;
192 
193     GET_HANDLE();
194 
195     for (tree=0; tree < ntree; ++tree) {
196 
197 	LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes);
198 	for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) {
199 	    if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
200 		for (i=0; values[i] != NULL; ++i) {
201 		    if (values[i])
202 		    if (krb5_ldap_parse_principal_name(values[i], &princ_name) != 0)
203 			continue;
204 		    if (krb5_parse_name(context, princ_name, &principal) != 0)
205 			continue;
206 		    if (is_principal_in_realm(ldap_context, principal) == 0) {
207 			if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, principal,
208 				    &entry)) != 0)
209 			    goto cleanup;
210 			(*func)(func_arg, &entry);
211 			krb5_dbe_free_contents(context, &entry);
212 			(void) krb5_free_principal(context, principal);
213 			if (princ_name)
214 			    free(princ_name);
215 			break;
216 		    }
217 		    (void) krb5_free_principal(context, principal);
218 		    if (princ_name)
219 			free(princ_name);
220 		}
221 		ldap_value_free(values);
222 	    }
223 	} /* end of for (ent= ... */
224 	ldap_msgfree(result);
225     } /* end of for (tree= ... */
226 
227 cleanup:
228     if (filter)
229 	free (filter);
230 
231     for (;ntree; --ntree)
232 	if (subtree[ntree-1])
233 	    free (subtree[ntree-1]);
234 
235     /* Solaris Kerberos: fix memory leak */
236     if (subtree != NULL) {
237 	free(subtree);
238     }
239     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
240     return st;
241 }
242 
243 
244 /*
245  * delete a principal from the directory.
246  */
247 krb5_error_code
248 krb5_ldap_delete_principal(context, searchfor, nentries)
249     krb5_context context;
250     krb5_const_principal searchfor;
251     int *nentries;		/* how many found & deleted */
252 {
253     char                      *user=NULL, *DN=NULL, *strval[10] = {NULL};
254     LDAPMod                   **mods=NULL;
255     LDAP                      *ld=NULL;
256     int 	              j=0, ptype=0, pcount=0;
257     unsigned int	      attrsetmask=0;
258     krb5_error_code           st=0;
259     krb5_boolean              singleentry=FALSE;
260     KEY                       *secretkey=NULL;
261     kdb5_dal_handle           *dal_handle=NULL;
262     krb5_ldap_context         *ldap_context=NULL;
263     krb5_ldap_server_handle   *ldap_server_handle=NULL;
264     krb5_db_entry             entries;
265     krb5_boolean              more=0;
266 
267     /* Clear the global error string */
268     krb5_clear_error_message(context);
269 
270     SETUP_CONTEXT();
271     /* get the principal info */
272     if ((st=krb5_ldap_get_principal(context, searchfor, &entries, nentries, &more)) != 0 || *nentries == 0)
273 	goto cleanup;
274 
275     if (((st=krb5_get_princ_type(context, &entries, &(ptype))) != 0) ||
276 	((st=krb5_get_attributes_mask(context, &entries, &(attrsetmask))) != 0) ||
277 	((st=krb5_get_princ_count(context, &entries, &(pcount))) != 0) ||
278 	((st=krb5_get_userdn(context, &entries, &(DN))) != 0))
279 	goto cleanup;
280 
281     if (DN == NULL) {
282 	st = EINVAL;
283 	krb5_set_error_message(context, st, gettext("DN information missing"));
284 	goto cleanup;
285     }
286 
287     GET_HANDLE();
288 
289     if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT) {
290         st = ldap_delete_ext_s(ld, DN, NULL, NULL);
291         if (st != LDAP_SUCCESS) {
292             st = set_ldap_error (context, st, OP_DEL);
293             goto cleanup;
294         }
295     } else {
296 	if (((st=krb5_unparse_name(context, searchfor, &user)) != 0)
297 	    || ((st=krb5_ldap_unparse_principal_name(user)) != 0))
298 	    goto cleanup;
299 
300 	memset(strval, 0, sizeof(strval));
301 	strval[0] = user;
302 	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_DELETE,
303 					  strval)) != 0)
304 	    goto cleanup;
305 
306 	singleentry = (pcount == 1) ? TRUE: FALSE;
307 	if (singleentry == FALSE) {
308 	    if (secretkey != NULL) {
309 		if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey", LDAP_MOD_DELETE | LDAP_MOD_BVALUES,
310 						  secretkey->keys)) != 0)
311 		    goto cleanup;
312 	    }
313 	} else {
314 	    /*
315 	     * If the Kerberos user principal to be deleted happens to be the last one associated
316 	     * with the directory user object, then it is time to delete the other kerberos
317 	     * specific attributes like krbmaxticketlife, i.e, unkerberize the directory user.
318 	     * From the attrsetmask value, identify the attributes set on the directory user
319 	     * object and delete them.
320 	     * NOTE: krbsecretkey attribute has per principal entries. There can be chances that the
321 	     * other principals' keys are exisiting/left-over. So delete all the values.
322 	     */
323 	    while (attrsetmask) {
324 		if (attrsetmask & 1) {
325 		    if ((st=krb5_add_str_mem_ldap_mod(&mods, attributes_set[j], LDAP_MOD_DELETE,
326 						      NULL)) != 0)
327 			goto cleanup;
328 		}
329 		attrsetmask >>= 1;
330 		++j;
331 	    }
332 
333 	    /* the same should be done with the objectclass attributes */
334 	    {
335 		char *attrvalues[] = {"krbticketpolicyaux", "krbprincipalaux", NULL};
336 /*		char *attrvalues[] = {"krbpwdpolicyrefaux", "krbticketpolicyaux", "krbprincipalaux", NULL};  */
337 		int p, q, r=0, amask=0;
338 
339 		if ((st=checkattributevalue(ld, DN, "objectclass", attrvalues, &amask)) != 0)
340 		    goto cleanup;
341 		memset(strval, 0, sizeof(strval));
342 		for (p=1, q=0; p<=4; p<<=1, ++q)
343 		    if (p & amask)
344 			strval[r++] = attrvalues[q];
345 		strval[r] = NULL;
346 		if (r > 0) {
347 		    if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_DELETE,
348 						      strval)) != 0)
349 			goto cleanup;
350 		}
351 	    }
352 	}
353 	st=ldap_modify_ext_s(ld, DN, mods, NULL, NULL);
354 	if (st != LDAP_SUCCESS) {
355 	    st = set_ldap_error(context, st, OP_MOD);
356 	    goto cleanup;
357 	}
358     }
359 
360 cleanup:
361     if (user)
362 	free (user);
363 
364     if (DN)
365 	free (DN);
366 
367     if (secretkey != NULL) {
368 	int i=0;
369 	while (i < secretkey->nkey) {
370 	    free (secretkey->keys[i]->bv_val);
371 	    free (secretkey->keys[i]);
372 	    ++i;
373 	}
374 	free (secretkey->keys);
375 	free (secretkey);
376     }
377 
378     if (st == 0)
379 	krb5_ldap_free_principal(context, &entries, *nentries);
380 
381     ldap_mods_free(mods, 1);
382     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
383     return st;
384 }
385 
386 
387 /*
388  * Function: krb5_ldap_unparse_principal_name
389  *
390  * Purpose: Removes '\\' that comes before every occurence of '@'
391  *          in the principal name component.
392  *
393  * Arguments:
394  *       user_name     (input/output)      Principal name
395  *
396  */
397 
398 krb5_error_code
399 krb5_ldap_unparse_principal_name(char *user_name)
400 {
401     char *tmp_princ_name=NULL, *princ_name=NULL, *tmp=NULL;
402     int l=0;
403     krb5_error_code st=0;
404 
405     if (strstr(user_name, "\\@")) {
406 
407 	tmp_princ_name = strdup(user_name);
408 	if (!tmp_princ_name) {
409 	    st = ENOMEM;
410 	    goto cleanup;
411 	}
412 	tmp = tmp_princ_name;
413 
414 	princ_name = (char *) malloc (strlen(user_name));
415 	if (!princ_name) {
416 	    st = ENOMEM;
417 	    goto cleanup;
418 	}
419 	memset(princ_name, 0, strlen(user_name));
420 
421 	l = 0;
422 	while (*tmp_princ_name) {
423 	    if ((*tmp_princ_name == '\\') && (*(tmp_princ_name+1) == '@')) {
424 		tmp_princ_name += 1;
425 	    } else {
426 		*(princ_name + l) = *tmp_princ_name++;
427 		l++;
428 	    }
429 	}
430 
431 	memset(user_name, 0, strlen(user_name));
432 	/*LINTED*/
433 	sprintf(user_name, "%s", princ_name);
434     }
435 
436 cleanup:
437     if (tmp) {
438 	free(tmp);
439 	tmp = NULL;
440     }
441 
442     if (princ_name) {
443 	free(princ_name);
444 	princ_name = NULL;
445     }
446 
447     return st;
448 }
449 
450 
451 /*
452  * Function: krb5_ldap_parse_principal_name
453  *
454  * Purpose: Inserts '\\' before every occurence of '@'
455  *          in the principal name component.
456  *
457  * Arguments:
458  *       i_princ_name     (input)      Principal name without '\\'
459  *       o_princ_name     (output)     Principal name with '\\'
460  *
461  * Note: The caller has to free the memory allocated for o_princ_name.
462  */
463 
464 krb5_error_code
465 krb5_ldap_parse_principal_name(i_princ_name, o_princ_name)
466     char              *i_princ_name;
467     char              **o_princ_name;
468 {
469     char *tmp_princ_name = NULL, *princ_name = NULL, *at_rlm_name = NULL;
470     int l = 0, m = 0, tmp_princ_name_len = 0, princ_name_len = 0, at_count = 0;
471     krb5_error_code st = 0;
472 
473     at_rlm_name = strrchr(i_princ_name, '@');
474 
475     if (!at_rlm_name) {
476 	*o_princ_name = strdup(i_princ_name);
477 	if (!o_princ_name) {
478 	    st = ENOMEM;
479 	    goto cleanup;
480 	}
481     } else {
482 	tmp_princ_name_len = at_rlm_name - i_princ_name;
483 
484 	tmp_princ_name = (char *) malloc ((unsigned) tmp_princ_name_len + 1);
485 	if (!tmp_princ_name) {
486 	    st = ENOMEM;
487 	    goto cleanup;
488 	}
489 	memset(tmp_princ_name, 0, (unsigned) tmp_princ_name_len + 1);
490 	memcpy(tmp_princ_name, i_princ_name, (unsigned) tmp_princ_name_len);
491 
492 	l = 0;
493 	while (tmp_princ_name[l]) {
494 	    if (tmp_princ_name[l++] == '@')
495 		at_count++;
496 	}
497 
498 	princ_name_len = strlen(i_princ_name) + at_count + 1;
499 	princ_name = (char *) malloc ((unsigned) princ_name_len);
500 	if (!princ_name) {
501 	    st = ENOMEM;
502 	    goto cleanup;
503 	}
504 	memset(princ_name, 0, (unsigned) princ_name_len);
505 
506 	l = 0;
507 	m = 0;
508 	while (tmp_princ_name[l]) {
509 	    if (tmp_princ_name[l] == '@') {
510 		princ_name[m++]='\\';
511 	    }
512 	    princ_name[m++]=tmp_princ_name[l++];
513 	}
514 	/* Solaris Kerberos: using strlcat for safety */
515 	strlcat(princ_name, at_rlm_name, princ_name_len);
516 
517 	*o_princ_name = princ_name;
518     }
519 
520 cleanup:
521 
522     if (tmp_princ_name) {
523 	free(tmp_princ_name);
524 	tmp_princ_name = NULL;
525     }
526 
527     return st;
528 }
529