1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/ldap_pwd_policy.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 2006 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_pwd_policy.h"
38 #include "ldap_err.h"
39
40 static char *password_policy_attributes[] = { "cn", "krbmaxpwdlife", "krbminpwdlife",
41 "krbpwdmindiffchars", "krbpwdminlength",
42 "krbpwdhistorylength", "krbpwdmaxfailure",
43 "krbpwdfailurecountinterval",
44 "krbpwdlockoutduration",
45 "krbpwdattributes",
46 "krbpwdmaxlife",
47 "krbpwdmaxrenewablelife",
48 "krbpwdallowedkeysalts", NULL };
49
50 /* Fill in mods with LDAP operations for the fields of policy, using the
51 * modification type op. mods must be freed by the caller on error. */
52 static krb5_error_code
add_policy_mods(krb5_context context,LDAPMod *** mods,osa_policy_ent_t policy,int op)53 add_policy_mods(krb5_context context, LDAPMod ***mods, osa_policy_ent_t policy,
54 int op)
55 {
56 krb5_error_code st;
57 char *strval[2] = { NULL };
58
59 st = krb5_add_int_mem_ldap_mod(mods, "krbmaxpwdlife", op,
60 (int)policy->pw_max_life);
61 if (st)
62 return st;
63
64 st = krb5_add_int_mem_ldap_mod(mods, "krbminpwdlife", op,
65 (int)policy->pw_min_life);
66 if (st)
67 return st;
68
69 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdmindiffchars", op,
70 (int)policy->pw_min_classes);
71 if (st)
72 return st;
73
74 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdminlength", op,
75 (int)policy->pw_min_length);
76 if (st)
77 return st;
78
79 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdhistorylength", op,
80 (int)policy->pw_history_num);
81 if (st)
82 return st;
83
84 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdmaxfailure", op,
85 (int)policy->pw_max_fail);
86 if (st)
87 return st;
88
89 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdfailurecountinterval", op,
90 (int)policy->pw_failcnt_interval);
91 if (st)
92 return st;
93
94 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdlockoutduration", op,
95 (int)policy->pw_lockout_duration);
96 if (st)
97 return st;
98
99 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdattributes", op,
100 (int)policy->attributes);
101 if (st)
102 return st;
103
104 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdmaxlife", op,
105 (int)policy->max_life);
106 if (st)
107 return st;
108
109 st = krb5_add_int_mem_ldap_mod(mods, "krbpwdmaxrenewablelife", op,
110 (int)policy->max_renewable_life);
111 if (st)
112 return st;
113
114 if (policy->allowed_keysalts != NULL) {
115 strval[0] = policy->allowed_keysalts;
116 st = krb5_add_str_mem_ldap_mod(mods, "krbpwdallowedkeysalts",
117 op, strval);
118 if (st)
119 return st;
120 }
121
122 /*
123 * Each policy tl-data type we add should be explicitly marshalled here.
124 * Unlike principals, we do not marshal unrecognized policy tl-data.
125 */
126
127 return 0;
128 }
129
130 /*
131 * Function to create password policy object.
132 */
133
134 krb5_error_code
krb5_ldap_create_password_policy(krb5_context context,osa_policy_ent_t policy)135 krb5_ldap_create_password_policy(krb5_context context, osa_policy_ent_t policy)
136 {
137 krb5_error_code st=0;
138 LDAP *ld=NULL;
139 LDAPMod **mods={NULL};
140 kdb5_dal_handle *dal_handle=NULL;
141 krb5_ldap_context *ldap_context=NULL;
142 krb5_ldap_server_handle *ldap_server_handle=NULL;
143 char *strval[2]={NULL}, *policy_dn=NULL;
144
145 /* Clear the global error string */
146 krb5_clear_error_message(context);
147
148 /* validate the input parameters */
149 if (policy == NULL || policy->name == NULL)
150 return EINVAL;
151
152 SETUP_CONTEXT();
153 GET_HANDLE();
154
155 st = krb5_ldap_name_to_policydn (context, policy->name, &policy_dn);
156 if (st != 0)
157 goto cleanup;
158
159 strval[0] = policy->name;
160 if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
161 goto cleanup;
162
163 strval[0] = "krbPwdPolicy";
164 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
165 goto cleanup;
166
167 st = add_policy_mods(context, &mods, policy, LDAP_MOD_ADD);
168 if (st)
169 goto cleanup;
170
171 /* password policy object creation */
172 if ((st=ldap_add_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
173 st = set_ldap_error (context, st, OP_ADD);
174 goto cleanup;
175 }
176
177 cleanup:
178 free(policy_dn);
179 ldap_mods_free(mods, 1);
180 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
181 return(st);
182 }
183
184 /*
185 * Function to modify password policy object.
186 */
187
188 krb5_error_code
krb5_ldap_put_password_policy(krb5_context context,osa_policy_ent_t policy)189 krb5_ldap_put_password_policy(krb5_context context, osa_policy_ent_t policy)
190 {
191 char *policy_dn=NULL;
192 krb5_error_code st=0;
193 LDAP *ld=NULL;
194 LDAPMod **mods=NULL;
195 kdb5_dal_handle *dal_handle=NULL;
196 krb5_ldap_context *ldap_context=NULL;
197 krb5_ldap_server_handle *ldap_server_handle=NULL;
198
199 /* Clear the global error string */
200 krb5_clear_error_message(context);
201
202 /* validate the input parameters */
203 if (policy == NULL || policy->name == NULL)
204 return EINVAL;
205
206 SETUP_CONTEXT();
207 GET_HANDLE();
208
209 st = krb5_ldap_name_to_policydn (context, policy->name, &policy_dn);
210 if (st != 0)
211 goto cleanup;
212
213 st = add_policy_mods(context, &mods, policy, LDAP_MOD_REPLACE);
214 if (st)
215 goto cleanup;
216
217 /* modify the password policy object. */
218 /*
219 * This will fail if the 'policy_dn' is anywhere other than under the realm
220 * container. This is correct behaviour. 'kdb5_ldap_util' will support
221 * management of only such policy objects.
222 */
223 if ((st=ldap_modify_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
224 st = set_ldap_error (context, st, OP_MOD);
225 goto cleanup;
226 }
227
228 cleanup:
229 free(policy_dn);
230 ldap_mods_free(mods, 1);
231 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
232 return(st);
233 }
234
235 static void
get_ui4(LDAP * ld,LDAPMessage * ent,char * name,krb5_ui_4 * out)236 get_ui4(LDAP *ld, LDAPMessage *ent, char *name, krb5_ui_4 *out)
237 {
238 int val;
239
240 krb5_ldap_get_value(ld, ent, name, &val);
241 *out = val;
242 }
243
244 static krb5_error_code
populate_policy(krb5_context context,LDAP * ld,LDAPMessage * ent,char * pol_name,osa_policy_ent_t pol_entry)245 populate_policy(krb5_context context,
246 LDAP *ld,
247 LDAPMessage *ent,
248 char *pol_name,
249 osa_policy_ent_t pol_entry)
250 {
251 int st = 0;
252
253 pol_entry->name = strdup(pol_name);
254 CHECK_NULL(pol_entry->name);
255 pol_entry->version = 1;
256
257 get_ui4(ld, ent, "krbmaxpwdlife", &pol_entry->pw_max_life);
258 get_ui4(ld, ent, "krbminpwdlife", &pol_entry->pw_min_life);
259 get_ui4(ld, ent, "krbpwdmindiffchars", &pol_entry->pw_min_classes);
260 get_ui4(ld, ent, "krbpwdminlength", &pol_entry->pw_min_length);
261 get_ui4(ld, ent, "krbpwdhistorylength", &pol_entry->pw_history_num);
262 get_ui4(ld, ent, "krbpwdmaxfailure", &pol_entry->pw_max_fail);
263 get_ui4(ld, ent, "krbpwdfailurecountinterval",
264 &pol_entry->pw_failcnt_interval);
265 get_ui4(ld, ent, "krbpwdlockoutduration", &pol_entry->pw_lockout_duration);
266 get_ui4(ld, ent, "krbpwdattributes", &pol_entry->attributes);
267 get_ui4(ld, ent, "krbpwdmaxlife", &pol_entry->max_life);
268 get_ui4(ld, ent, "krbpwdmaxrenewablelife", &pol_entry->max_renewable_life);
269
270 st = krb5_ldap_get_string(ld, ent, "krbpwdallowedkeysalts",
271 &(pol_entry->allowed_keysalts), NULL);
272 if (st)
273 goto cleanup;
274 /*
275 * We don't store the policy refcnt, because principals might be maintained
276 * outside of kadmin. Instead, we will check for principal references when
277 * policies are deleted.
278 */
279 pol_entry->policy_refcnt = 0;
280
281 cleanup:
282 return st;
283 }
284
285 static krb5_error_code
krb5_ldap_get_password_policy_from_dn(krb5_context context,char * pol_name,char * pol_dn,osa_policy_ent_t * policy)286 krb5_ldap_get_password_policy_from_dn(krb5_context context, char *pol_name,
287 char *pol_dn, osa_policy_ent_t *policy)
288 {
289 krb5_error_code st=0, tempst=0;
290 LDAP *ld=NULL;
291 LDAPMessage *result=NULL,*ent=NULL;
292 kdb5_dal_handle *dal_handle=NULL;
293 krb5_ldap_context *ldap_context=NULL;
294 krb5_ldap_server_handle *ldap_server_handle=NULL;
295
296 /* Clear the global error string */
297 krb5_clear_error_message(context);
298
299 /* validate the input parameters */
300 if (pol_dn == NULL)
301 return EINVAL;
302
303 *policy = NULL;
304 SETUP_CONTEXT();
305 GET_HANDLE();
306
307 *(policy) = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec));
308 if (*policy == NULL) {
309 st = ENOMEM;
310 goto cleanup;
311 }
312 memset(*policy, 0, sizeof(osa_policy_ent_rec));
313
314 LDAP_SEARCH(pol_dn, LDAP_SCOPE_BASE, "(objectclass=krbPwdPolicy)", password_policy_attributes);
315
316 ent=ldap_first_entry(ld, result);
317 if (ent == NULL) {
318 st = KRB5_KDB_NOENTRY;
319 goto cleanup;
320 }
321 st = populate_policy(context, ld, ent, pol_name, *policy);
322
323 cleanup:
324 ldap_msgfree(result);
325 if (st != 0) {
326 if (*policy != NULL) {
327 krb5_db_free_policy(context, *policy);
328 *policy = NULL;
329 }
330 }
331
332 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
333 return st;
334 }
335
336 /*
337 * Convert 'name' into a directory DN and call
338 * 'krb5_ldap_get_password_policy_from_dn'
339 */
340 krb5_error_code
krb5_ldap_get_password_policy(krb5_context context,char * name,osa_policy_ent_t * policy)341 krb5_ldap_get_password_policy(krb5_context context, char *name,
342 osa_policy_ent_t *policy)
343 {
344 krb5_error_code st = 0;
345 char *policy_dn = NULL;
346
347 /* Clear the global error string */
348 krb5_clear_error_message(context);
349
350 /* validate the input parameters */
351 if (name == NULL) {
352 st = EINVAL;
353 goto cleanup;
354 }
355
356 st = krb5_ldap_name_to_policydn(context, name, &policy_dn);
357 if (st != 0)
358 goto cleanup;
359
360 st = krb5_ldap_get_password_policy_from_dn(context, name, policy_dn,
361 policy);
362
363 cleanup:
364 free(policy_dn);
365 return st;
366 }
367
368 krb5_error_code
krb5_ldap_delete_password_policy(krb5_context context,char * policy)369 krb5_ldap_delete_password_policy(krb5_context context, char *policy)
370 {
371 int mask = 0;
372 char *policy_dn = NULL, *class[] = {"krbpwdpolicy", NULL};
373 krb5_error_code st=0;
374 LDAP *ld=NULL;
375 kdb5_dal_handle *dal_handle=NULL;
376 krb5_ldap_context *ldap_context=NULL;
377 krb5_ldap_server_handle *ldap_server_handle=NULL;
378
379 /* Clear the global error string */
380 krb5_clear_error_message(context);
381
382 /* validate the input parameters */
383 if (policy == NULL)
384 return EINVAL;
385
386 SETUP_CONTEXT();
387 GET_HANDLE();
388
389 st = krb5_ldap_name_to_policydn (context, policy, &policy_dn);
390 if (st != 0)
391 goto cleanup;
392
393 /* Ensure that the object is a password policy */
394 if ((st=checkattributevalue(ld, policy_dn, "objectclass", class, &mask)) != 0)
395 goto cleanup;
396
397 if (mask == 0) {
398 st = KRB5_KDB_NOENTRY;
399 goto cleanup;
400 }
401
402 if ((st=ldap_delete_ext_s(ld, policy_dn, NULL, NULL)) != LDAP_SUCCESS) {
403 st = set_ldap_error (context, st, OP_DEL);
404 goto cleanup;
405 }
406
407 cleanup:
408 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
409 free(policy_dn);
410
411 return st;
412 }
413
414 krb5_error_code
krb5_ldap_iterate_password_policy(krb5_context context,char * match_expr,void (* func)(krb5_pointer,osa_policy_ent_t),krb5_pointer func_arg)415 krb5_ldap_iterate_password_policy(krb5_context context, char *match_expr,
416 void (*func)(krb5_pointer, osa_policy_ent_t),
417 krb5_pointer func_arg)
418 {
419 osa_policy_ent_rec *entry=NULL;
420 char *policy=NULL;
421 krb5_error_code st=0, tempst=0;
422 LDAP *ld=NULL;
423 LDAPMessage *result=NULL, *ent=NULL;
424 kdb5_dal_handle *dal_handle=NULL;
425 krb5_ldap_context *ldap_context=NULL;
426 krb5_ldap_server_handle *ldap_server_handle=NULL;
427
428 /* Clear the global error string */
429 krb5_clear_error_message(context);
430
431 SETUP_CONTEXT();
432 GET_HANDLE();
433
434 if (ldap_context->lrparams->realmdn == NULL) {
435 st = EINVAL;
436 goto cleanup;
437 }
438
439 LDAP_SEARCH(ldap_context->lrparams->realmdn, LDAP_SCOPE_ONELEVEL, "(objectclass=krbpwdpolicy)", password_policy_attributes);
440 for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) {
441 krb5_boolean attr_present;
442
443 st = krb5_ldap_get_string(ld, ent, "cn", &policy, &attr_present);
444 if (st != 0)
445 goto cleanup;
446 if (attr_present == FALSE)
447 continue;
448
449 entry = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec));
450 CHECK_NULL(entry);
451 memset(entry, 0, sizeof(osa_policy_ent_rec));
452 if ((st = populate_policy(context, ld, ent, policy, entry)) != 0)
453 goto cleanup;
454
455 (*func)(func_arg, entry);
456 krb5_db_free_policy(context, entry);
457 entry = NULL;
458
459 free(policy);
460 policy = NULL;
461 }
462
463 cleanup:
464 free(entry);
465 free(policy);
466 ldap_msgfree(result);
467 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
468 return st;
469 }
470