xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/nssov/pam.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: pam.c,v 1.1.1.2 2010/12/12 15:19:10 adam Exp $	*/
2 
3 /* pam.c - pam processing routines */
4 /* OpenLDAP: pkg/ldap/contrib/slapd-modules/nssov/pam.c,v 1.13.2.8 2010/04/15 21:32:56 quanah Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2008-2010 The OpenLDAP Foundation.
8  * Portions Copyright 2008 by Howard Chu, Symas Corp.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 
20 #include "nssov.h"
21 #include "lutil.h"
22 
23 static int ppolicy_cid;
24 static AttributeDescription *ad_loginStatus;
25 
26 struct paminfo {
27 	struct berval uid;
28 	struct berval dn;
29 	struct berval svc;
30 	struct berval pwd;
31 	int authz;
32 	struct berval msg;
33 };
34 
35 static int pam_bindcb(
36 	Operation *op, SlapReply *rs)
37 {
38 	struct paminfo *pi = op->o_callback->sc_private;
39 	LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
40 		rs->sr_ctrls, NULL);
41 	if (ctrl) {
42 		LDAP *ld;
43 		ber_int_t expire, grace;
44 		LDAPPasswordPolicyError error;
45 
46 		ldap_create(&ld);
47 		if (ld) {
48 			int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
49 				&expire,&grace,&error);
50 			if (rc == LDAP_SUCCESS) {
51 				if (expire >= 0) {
52 					char *unit = "seconds";
53 					if (expire > 60) {
54 						expire /= 60;
55 						unit = "minutes";
56 					}
57 					if (expire > 60) {
58 						expire /= 60;
59 						unit = "hours";
60 					}
61 					if (expire > 24) {
62 						expire /= 24;
63 						unit = "days";
64 					}
65 #if 0	/* Who warns about expiration so far in advance? */
66 					if (expire > 7) {
67 						expire /= 7;
68 						unit = "weeks";
69 					}
70 					if (expire > 4) {
71 						expire /= 4;
72 						unit = "months";
73 					}
74 					if (expire > 12) {
75 						expire /= 12;
76 						unit = "years";
77 					}
78 #endif
79 					pi->msg.bv_len = sprintf(pi->msg.bv_val,
80 						"\nWARNING: Password expires in %d %s\n", expire, unit);
81 				} else if (grace > 0) {
82 					pi->msg.bv_len = sprintf(pi->msg.bv_val,
83 						"Password expired; %d grace logins remaining",
84 						grace);
85 					pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
86 				} else if (error != PP_noError) {
87 					ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
88 						&pi->msg);
89 					switch (error) {
90 					case PP_passwordExpired:
91 						/* report this during authz */
92 						rs->sr_err = LDAP_SUCCESS;
93 						/* fallthru */
94 					case PP_changeAfterReset:
95 						pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
96 					}
97 				}
98 			}
99 			ldap_ld_free(ld,0,NULL,NULL);
100 		}
101 	}
102 	return LDAP_SUCCESS;
103 }
104 
105 static int pam_uid2dn(nssov_info *ni, Operation *op,
106 	struct paminfo *pi)
107 {
108 	struct berval sdn;
109 
110 	BER_BVZERO(&pi->dn);
111 
112 	if (!isvalidusername(&pi->uid)) {
113 		Debug(LDAP_DEBUG_ANY,"nssov_pam_uid2dn(%s): invalid user name\n",
114 			pi->uid.bv_val,0,0);
115 		return NSLCD_PAM_USER_UNKNOWN;
116 	}
117 
118 	if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
119 		int hlen = global_host_bv.bv_len;
120 
121 		/* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
122 		sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
123 			STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
124 		sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
125 		sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
126 			pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
127 		slap_sasl2dn(op, &sdn, &pi->dn, 0);
128 		op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
129 	}
130 
131 	/* If no luck, do a basic uid search */
132 	if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
133 		nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
134 		if (!BER_BVISEMPTY(&pi->dn)) {
135 			sdn = pi->dn;
136 			dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
137 		}
138 	}
139 	if (BER_BVISEMPTY(&pi->dn)) {
140 		return NSLCD_PAM_USER_UNKNOWN;
141 	}
142 	return 0;
143 }
144 
145 int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
146 	struct paminfo *pi)
147 {
148 	int rc;
149 	slap_callback cb = {0};
150 	SlapReply rs = {REP_RESULT};
151 
152 	pi->msg.bv_val = pi->pwd.bv_val;
153 	pi->msg.bv_len = 0;
154 	pi->authz = NSLCD_PAM_SUCCESS;
155 	BER_BVZERO(&pi->dn);
156 
157 	rc = pam_uid2dn(ni, op, pi);
158 	if (rc) goto finish;
159 
160 	if (BER_BVISEMPTY(&pi->pwd)) {
161 		rc = NSLCD_PAM_IGNORE;
162 		goto finish;
163 	}
164 
165 	/* Should only need to do this once at open time, but there's always
166 	 * the possibility that ppolicy will get loaded later.
167 	 */
168 	if (!ppolicy_cid) {
169 		rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
170 			&ppolicy_cid);
171 	}
172 	/* of course, 0 is a valid cid, but it won't be ppolicy... */
173 	if (ppolicy_cid) {
174 		op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
175 	}
176 	cb.sc_response = pam_bindcb;
177 	cb.sc_private = pi;
178 	op->o_callback = &cb;
179 	op->o_dn.bv_val[0] = 0;
180 	op->o_dn.bv_len = 0;
181 	op->o_ndn.bv_val[0] = 0;
182 	op->o_ndn.bv_len = 0;
183 	op->o_tag = LDAP_REQ_BIND;
184 	op->o_protocol = LDAP_VERSION3;
185 	op->orb_method = LDAP_AUTH_SIMPLE;
186 	op->orb_cred = pi->pwd;
187 	op->o_req_dn = pi->dn;
188 	op->o_req_ndn = pi->dn;
189 	slap_op_time( &op->o_time, &op->o_tincr );
190 	rc = op->o_bd->be_bind( op, &rs );
191 	memset(pi->pwd.bv_val,0,pi->pwd.bv_len);
192 	/* quirk: on successful bind, caller has to send result. we need
193 	 * to make sure callbacks run.
194 	 */
195 	if (rc == LDAP_SUCCESS)
196 		send_ldap_result(op, &rs);
197 	switch(rs.sr_err) {
198 	case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
199 	case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
200 	default: rc = NSLCD_PAM_AUTH_ERR; break;
201 	}
202 finish:
203 	return rc;
204 }
205 
206 int pam_authc(nssov_info *ni,TFILE *fp,Operation *op)
207 {
208 	int32_t tmpint32;
209 	int rc;
210 	slap_callback cb = {0};
211 	SlapReply rs = {REP_RESULT};
212 	char dnc[1024];
213 	char uidc[32];
214 	char svcc[256];
215 	char pwdc[256];
216 	struct berval sdn, dn;
217 	struct paminfo pi;
218 
219 
220 	READ_STRING(fp,uidc);
221 	pi.uid.bv_val = uidc;
222 	pi.uid.bv_len = tmpint32;
223 	READ_STRING(fp,dnc);
224 	pi.dn.bv_val = dnc;
225 	pi.dn.bv_len = tmpint32;
226 	READ_STRING(fp,svcc);
227 	pi.svc.bv_val = svcc;
228 	pi.svc.bv_len = tmpint32;
229 	READ_STRING(fp,pwdc);
230 	pi.pwd.bv_val = pwdc;
231 	pi.pwd.bv_len = tmpint32;
232 
233 	Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",pi.uid.bv_val,0,0);
234 
235 	rc = pam_do_bind(ni, fp, op, &pi);
236 
237 finish:
238 	WRITE_INT32(fp,NSLCD_VERSION);
239 	WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
240 	WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
241 	WRITE_BERVAL(fp,&pi.uid);
242 	WRITE_BERVAL(fp,&pi.dn);
243 	WRITE_INT32(fp,rc);
244 	WRITE_INT32(fp,pi.authz);	/* authz */
245 	WRITE_BERVAL(fp,&pi.msg);	/* authzmsg */
246 	return 0;
247 }
248 
249 static struct berval grpmsg =
250 	BER_BVC("Access denied by group check");
251 static struct berval hostmsg =
252 	BER_BVC("Access denied for this host");
253 static struct berval svcmsg =
254 	BER_BVC("Access denied for this service");
255 static struct berval uidmsg =
256 	BER_BVC("Access denied by UID check");
257 
258 static int pam_compare_cb(Operation *op, SlapReply *rs)
259 {
260 	if (rs->sr_err == LDAP_COMPARE_TRUE)
261 		op->o_callback->sc_private = (void *)1;
262 	return LDAP_SUCCESS;
263 }
264 
265 int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
266 {
267 	struct berval dn, uid, svc, ruser, rhost, tty;
268 	struct berval authzmsg = BER_BVNULL;
269 	int32_t tmpint32;
270 	char dnc[1024];
271 	char uidc[32];
272 	char svcc[256];
273 	char ruserc[32];
274 	char rhostc[256];
275 	char ttyc[256];
276 	int rc;
277 	Entry *e = NULL;
278 	Attribute *a;
279 	SlapReply rs = {REP_RESULT};
280 	slap_callback cb = {0};
281 
282 	READ_STRING(fp,uidc);
283 	uid.bv_val = uidc;
284 	uid.bv_len = tmpint32;
285 	READ_STRING(fp,dnc);
286 	dn.bv_val = dnc;
287 	dn.bv_len = tmpint32;
288 	READ_STRING(fp,svcc);
289 	svc.bv_val = svcc;
290 	svc.bv_len = tmpint32;
291 	READ_STRING(fp,ruserc);
292 	ruser.bv_val = ruserc;
293 	ruser.bv_len = tmpint32;
294 	READ_STRING(fp,rhostc);
295 	rhost.bv_val = rhostc;
296 	rhost.bv_len = tmpint32;
297 	READ_STRING(fp,ttyc);
298 	tty.bv_val = ttyc;
299 	tty.bv_len = tmpint32;
300 
301 	Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",dn.bv_val,0,0);
302 
303 	/* If we didn't do authc, we don't have a DN yet */
304 	if (BER_BVISEMPTY(&dn)) {
305 		struct paminfo pi;
306 		pi.uid = uid;
307 		pi.svc = svc;
308 
309 		rc = pam_uid2dn(ni, op, &pi);
310 		if (rc) goto finish;
311 		dn = pi.dn;
312 	}
313 
314 	/* See if they have access to the host and service */
315 	if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
316 		AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
317 		struct berval hostdn = BER_BVNULL;
318 		struct berval odn = op->o_ndn;
319 		op->o_dn = dn;
320 		op->o_ndn = dn;
321 		{
322 			nssov_mapinfo *mi = &ni->ni_maps[NM_host];
323 			char fbuf[1024];
324 			struct berval filter = {sizeof(fbuf),fbuf};
325 			SlapReply rs2 = {REP_RESULT};
326 
327 			/* Lookup the host entry */
328 			nssov_filter_byname(mi,0,&global_host_bv,&filter);
329 			cb.sc_private = &hostdn;
330 			cb.sc_response = nssov_name2dn_cb;
331 			op->o_callback = &cb;
332 			op->o_req_dn = mi->mi_base;
333 			op->o_req_ndn = mi->mi_base;
334 			op->ors_scope = mi->mi_scope;
335 			op->ors_filterstr = filter;
336 			op->ors_filter = str2filter_x(op, filter.bv_val);
337 			op->ors_attrs = slap_anlist_no_attrs;
338 			op->ors_tlimit = SLAP_NO_LIMIT;
339 			op->ors_slimit = 2;
340 			rc = op->o_bd->be_search(op, &rs2);
341 			filter_free_x(op, op->ors_filter, 1);
342 
343 			if (BER_BVISEMPTY(&hostdn) &&
344 				!BER_BVISEMPTY(&ni->ni_pam_defhost)) {
345 				filter.bv_len = sizeof(fbuf);
346 				filter.bv_val = fbuf;
347 				memset(&rs2, 0, sizeof(rs2));
348 				rs2.sr_type = REP_RESULT;
349 				nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
350 				op->ors_filterstr = filter;
351 				op->ors_filter = str2filter_x(op, filter.bv_val);
352 				rc = op->o_bd->be_search(op, &rs2);
353 				filter_free_x(op, op->ors_filter, 1);
354 			}
355 
356 			/* no host entry, no default host -> deny */
357 			if (BER_BVISEMPTY(&hostdn)) {
358 				rc = NSLCD_PAM_PERM_DENIED;
359 				authzmsg = hostmsg;
360 				goto finish;
361 			}
362 		}
363 
364 		cb.sc_response = pam_compare_cb;
365 		cb.sc_private = NULL;
366 		op->o_tag = LDAP_REQ_COMPARE;
367 		op->o_req_dn = hostdn;
368 		op->o_req_ndn = hostdn;
369 		ava.aa_desc = nssov_pam_svc_ad;
370 		ava.aa_value = svc;
371 		op->orc_ava = &ava;
372 		rc = op->o_bd->be_compare( op, &rs );
373 		if ( cb.sc_private == NULL ) {
374 			authzmsg = svcmsg;
375 			rc = NSLCD_PAM_PERM_DENIED;
376 			goto finish;
377 		}
378 		op->o_dn = odn;
379 		op->o_ndn = odn;
380 	}
381 
382 	/* See if they're a member of the group */
383 	if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
384 		!BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
385 		ni->ni_pam_group_ad) {
386 		AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
387 		op->o_callback = &cb;
388 		cb.sc_response = slap_null_cb;
389 		op->o_tag = LDAP_REQ_COMPARE;
390 		op->o_req_dn = ni->ni_pam_group_dn;
391 		op->o_req_ndn = ni->ni_pam_group_dn;
392 		ava.aa_desc = ni->ni_pam_group_ad;
393 		ava.aa_value = dn;
394 		op->orc_ava = &ava;
395 		rc = op->o_bd->be_compare( op, &rs );
396 		if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
397 			authzmsg = grpmsg;
398 			rc = NSLCD_PAM_PERM_DENIED;
399 			goto finish;
400 		}
401 	}
402 
403 	/* We need to check the user's entry for these bits */
404 	if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
405 		ni->ni_pam_template_ad ||
406 		ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
407 		rc = be_entry_get_rw( op, &dn, NULL, NULL, 0, &e );
408 		if (rc != LDAP_SUCCESS) {
409 			rc = NSLCD_PAM_USER_UNKNOWN;
410 			goto finish;
411 		}
412 	}
413 	if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
414 		a = attr_find(e->e_attrs, nssov_pam_host_ad);
415 		if (!a || attr_valfind( a,
416 			SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
417 			SLAP_MR_VALUE_OF_SYNTAX,
418 			&global_host_bv, NULL, op->o_tmpmemctx )) {
419 			rc = NSLCD_PAM_PERM_DENIED;
420 			authzmsg = hostmsg;
421 			goto finish;
422 		}
423 	}
424 	if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
425 		a = attr_find(e->e_attrs, nssov_pam_svc_ad);
426 		if (!a || attr_valfind( a,
427 			SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
428 			SLAP_MR_VALUE_OF_SYNTAX,
429 			&svc, NULL, op->o_tmpmemctx )) {
430 			rc = NSLCD_PAM_PERM_DENIED;
431 			authzmsg = svcmsg;
432 			goto finish;
433 		}
434 	}
435 
436 /* from passwd.c */
437 #define UIDN_KEY	2
438 
439 	if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
440 		int id;
441 		char *tmp;
442 		nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
443 		a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
444 		if (!a) {
445 			rc = NSLCD_PAM_PERM_DENIED;
446 			authzmsg = uidmsg;
447 			goto finish;
448 		}
449 		id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
450 		if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
451 			rc = NSLCD_PAM_PERM_DENIED;
452 			authzmsg = uidmsg;
453 			goto finish;
454 		}
455 		if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
456 			(ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
457 			rc = NSLCD_PAM_PERM_DENIED;
458 			authzmsg = uidmsg;
459 			goto finish;
460 		}
461 	}
462 
463 	if (ni->ni_pam_template_ad) {
464 		a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
465 		if (a)
466 			uid = a->a_vals[0];
467 		else if (!BER_BVISEMPTY(&ni->ni_pam_template))
468 			uid = ni->ni_pam_template;
469 	}
470 	rc = NSLCD_PAM_SUCCESS;
471 
472 finish:
473 	WRITE_INT32(fp,NSLCD_VERSION);
474 	WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
475 	WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
476 	WRITE_BERVAL(fp,&uid);
477 	WRITE_BERVAL(fp,&dn);
478 	WRITE_INT32(fp,rc);
479 	WRITE_BERVAL(fp,&authzmsg);
480 	if (e) {
481 		be_entry_release_r(op, e);
482 	}
483 	return 0;
484 }
485 
486 static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
487 {
488 	struct berval dn, uid, svc, tty, rhost, ruser;
489 	int32_t tmpint32;
490 	char dnc[1024];
491 	char svcc[256];
492 	char uidc[32];
493 	char ttyc[32];
494 	char rhostc[256];
495 	char ruserc[32];
496 	slap_callback cb = {0};
497 	SlapReply rs = {REP_RESULT};
498 	char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
499 	struct berval timestamp, bv[2], *nbv;
500 	time_t stamp;
501 	Modifications mod;
502 
503 	READ_STRING(fp,uidc);
504 	uid.bv_val = uidc;
505 	uid.bv_len = tmpint32;
506 	READ_STRING(fp,dnc);
507 	dn.bv_val = dnc;
508 	dn.bv_len = tmpint32;
509 	READ_STRING(fp,svcc);
510 	svc.bv_val = svcc;
511 	svc.bv_len = tmpint32;
512 	READ_STRING(fp,ttyc);
513 	tty.bv_val = ttyc;
514 	tty.bv_len = tmpint32;
515 	READ_STRING(fp,rhostc);
516 	rhost.bv_val = rhostc;
517 	rhost.bv_len = tmpint32;
518 	READ_STRING(fp,ruserc);
519 	ruser.bv_val = ruserc;
520 	ruser.bv_len = tmpint32;
521 	READ_INT32(fp,stamp);
522 
523 	Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
524 		action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', dn.bv_val,0);
525 
526 	if (!dn.bv_len || !ni->ni_pam_sessions) return 0;
527 
528 	{
529 		int i, found=0;
530 		for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
531 			if (ni->ni_pam_sessions[i].bv_len != svc.bv_len)
532 				continue;
533 			if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, svc.bv_val)) {
534 				found = 1;
535 				break;
536 			}
537 		}
538 		if (!found) return 0;
539 	}
540 
541 	slap_op_time( &op->o_time, &op->o_tincr );
542 	timestamp.bv_len = sizeof(timebuf);
543 	timestamp.bv_val = timebuf;
544 	if (action == NSLCD_ACTION_PAM_SESS_O )
545 		stamp = op->o_time;
546 	slap_timestamp( &stamp, &timestamp );
547 	bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + svc.bv_len +
548 		tty.bv_len + ruser.bv_len + rhost.bv_len + STRLENOF("    (@)");
549 	bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
550 	sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
551 		timestamp.bv_val, global_host_bv.bv_val, svc.bv_val, tty.bv_val,
552 		ruser.bv_val, rhost.bv_val);
553 
554 	mod.sml_numvals = 1;
555 	mod.sml_values = bv;
556 	BER_BVZERO(&bv[1]);
557 	attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
558 	mod.sml_nvalues = nbv;
559 	mod.sml_desc = ad_loginStatus;
560 	mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
561 		LDAP_MOD_DELETE;
562 	mod.sml_flags = SLAP_MOD_INTERNAL;
563 	mod.sml_next = NULL;
564 
565 	cb.sc_response = slap_null_cb;
566 	op->o_callback = &cb;
567 	op->o_tag = LDAP_REQ_MODIFY;
568 	op->o_dn = op->o_bd->be_rootdn;
569 	op->o_ndn = op->o_bd->be_rootndn;
570 	op->orm_modlist = &mod;
571 	op->orm_no_opattrs = 1;
572 	op->o_req_dn = dn;
573 	op->o_req_ndn = dn;
574 	op->o_bd->be_modify( op, &rs );
575 	if ( mod.sml_next ) {
576 		slap_mods_free( mod.sml_next, 1 );
577 	}
578 	ber_bvarray_free_x( nbv, op->o_tmpmemctx );
579 
580 	WRITE_INT32(fp,NSLCD_VERSION);
581 	WRITE_INT32(fp,action);
582 	WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
583 	WRITE_INT32(fp,op->o_time);
584 	return 0;
585 }
586 
587 int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
588 {
589 	return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
590 }
591 
592 int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
593 {
594 	return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
595 }
596 
597 int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op)
598 {
599 	struct berval npw;
600 	int32_t tmpint32;
601 	char dnc[1024];
602 	char uidc[32];
603 	char opwc[256];
604 	char npwc[256];
605 	char svcc[256];
606 	struct paminfo pi;
607 	int rc;
608 
609 	READ_STRING(fp,uidc);
610 	pi.uid.bv_val = uidc;
611 	pi.uid.bv_len = tmpint32;
612 	READ_STRING(fp,dnc);
613 	pi.dn.bv_val = dnc;
614 	pi.dn.bv_len = tmpint32;
615 	READ_STRING(fp,svcc);
616 	pi.svc.bv_val = svcc;
617 	pi.svc.bv_len = tmpint32;
618 	READ_STRING(fp,opwc);
619 	pi.pwd.bv_val = opwc;
620 	pi.pwd.bv_len = tmpint32;
621 	READ_STRING(fp,npwc);
622 	npw.bv_val = npwc;
623 	npw.bv_len = tmpint32;
624 
625 	Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s\n",
626 		pi.dn.bv_val,pi.uid.bv_val,0);
627 
628 	BER_BVZERO(&pi.msg);
629 
630 	/* This is a prelim check */
631 	if (BER_BVISEMPTY(&pi.dn)) {
632 		rc = pam_do_bind(ni,fp,op,&pi);
633 		if (rc == NSLCD_PAM_IGNORE)
634 			rc = NSLCD_PAM_SUCCESS;
635 	} else {
636 		BerElementBuffer berbuf;
637 		BerElement *ber = (BerElement *)&berbuf;
638 		struct berval bv;
639 		SlapReply rs = {REP_RESULT};
640 		slap_callback cb = {0};
641 
642 		ber_init_w_nullc(ber, LBER_USE_DER);
643 		ber_printf(ber, "{");
644 		if (!BER_BVISEMPTY(&pi.pwd))
645 			ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
646 				&pi.pwd);
647 		if (!BER_BVISEMPTY(&npw))
648 			ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
649 				&npw);
650 		ber_printf(ber, "N}");
651 		ber_flatten2(ber, &bv, 0);
652 		op->o_tag = LDAP_REQ_EXTENDED;
653 		op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
654 		op->ore_reqdata = &bv;
655 		op->o_dn = pi.dn;
656 		op->o_ndn = pi.dn;
657 		op->o_callback = &cb;
658 		op->o_conn->c_authz_backend = op->o_bd;
659 		cb.sc_response = slap_null_cb;
660 		op->o_bd = frontendDB;
661 		rc = op->o_bd->be_extended(op, &rs);
662 		if (rs.sr_text)
663 			ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
664 		if (rc == LDAP_SUCCESS)
665 			rc = NSLCD_PAM_SUCCESS;
666 		else
667 			rc = NSLCD_PAM_PERM_DENIED;
668 	}
669 	WRITE_INT32(fp,NSLCD_VERSION);
670 	WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
671 	WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
672 	WRITE_BERVAL(fp,&pi.uid);
673 	WRITE_BERVAL(fp,&pi.dn);
674 	WRITE_INT32(fp,rc);
675 	WRITE_BERVAL(fp,&pi.msg);
676 	return 0;
677 }
678 
679 int nssov_pam_init()
680 {
681 	int code = 0;
682 	const char *text;
683 	if (!ad_loginStatus)
684 		code = slap_str2ad( "loginStatus", &ad_loginStatus, &text );
685 
686 	return code;
687 }
688