1 /* pam.c - pam processing routines */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2008-2010 The OpenLDAP Foundation.
6 * Portions Copyright 2008 by Howard Chu, Symas Corp.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
21 static int ppolicy_cid;
22 static AttributeDescription *ad_loginStatus;
33 static int pam_bindcb(
34 Operation *op, SlapReply *rs)
36 struct paminfo *pi = op->o_callback->sc_private;
37 LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
41 ber_int_t expire, grace;
42 LDAPPasswordPolicyError error;
46 int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
47 &expire,&grace,&error);
48 if (rc == LDAP_SUCCESS) {
50 char *unit = "seconds";
63 #if 0 /* Who warns about expiration so far in advance? */
77 pi->msg.bv_len = sprintf(pi->msg.bv_val,
78 "\nWARNING: Password expires in %d %s\n", expire, unit);
79 } else if (grace > 0) {
80 pi->msg.bv_len = sprintf(pi->msg.bv_val,
81 "Password expired; %d grace logins remaining",
83 pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
84 } else if (error != PP_noError) {
85 ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
88 case PP_passwordExpired:
89 /* report this during authz */
90 rs->sr_err = LDAP_SUCCESS;
92 case PP_changeAfterReset:
93 pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
97 ldap_ld_free(ld,0,NULL,NULL);
103 int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
107 slap_callback cb = {0};
108 SlapReply rs = {REP_RESULT};
111 pi->msg.bv_val = pi->pwd.bv_val;
113 pi->authz = NSLCD_PAM_SUCCESS;
116 if (!isvalidusername(&pi->uid)) {
117 Debug(LDAP_DEBUG_ANY,"nssov_pam_do_bind(%s): invalid user name\n",
119 rc = NSLCD_PAM_USER_UNKNOWN;
123 if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
124 int hlen = global_host_bv.bv_len;
126 /* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
127 sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
128 STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
129 sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
130 sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
131 pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
132 slap_sasl2dn(op, &sdn, &pi->dn, 0);
133 op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
136 /* If no luck, do a basic uid search */
137 if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
138 nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
139 if (!BER_BVISEMPTY(&pi->dn)) {
141 dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
145 if (BER_BVISEMPTY(&pi->dn)) {
146 rc = NSLCD_PAM_USER_UNKNOWN;
150 if (BER_BVISEMPTY(&pi->pwd)) {
151 rc = NSLCD_PAM_IGNORE;
155 /* Should only need to do this once at open time, but there's always
156 * the possibility that ppolicy will get loaded later.
159 rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
162 /* of course, 0 is a valid cid, but it won't be ppolicy... */
164 op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
166 cb.sc_response = pam_bindcb;
168 op->o_callback = &cb;
169 op->o_dn.bv_val[0] = 0;
171 op->o_ndn.bv_val[0] = 0;
172 op->o_ndn.bv_len = 0;
173 op->o_tag = LDAP_REQ_BIND;
174 op->o_protocol = LDAP_VERSION3;
175 op->orb_method = LDAP_AUTH_SIMPLE;
176 op->orb_cred = pi->pwd;
177 op->o_req_dn = pi->dn;
178 op->o_req_ndn = pi->dn;
179 slap_op_time( &op->o_time, &op->o_tincr );
180 rc = op->o_bd->be_bind( op, &rs );
181 memset(pi->pwd.bv_val,0,pi->pwd.bv_len);
182 /* quirk: on successful bind, caller has to send result. we need
183 * to make sure callbacks run.
185 if (rc == LDAP_SUCCESS)
186 send_ldap_result(op, &rs);
188 case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
189 case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
190 default: rc = NSLCD_PAM_AUTH_ERR; break;
196 int pam_authc(nssov_info *ni,TFILE *fp,Operation *op)
200 slap_callback cb = {0};
201 SlapReply rs = {REP_RESULT};
206 struct berval sdn, dn;
210 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
211 pi.uid.bv_val = uidc;
212 pi.uid.bv_len = tmpint32;
213 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
215 pi.dn.bv_len = tmpint32;
216 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
217 pi.svc.bv_val = svcc;
218 pi.svc.bv_len = tmpint32;
219 READ_STRING_BUF2(fp,pwdc,sizeof(pwdc));
220 pi.pwd.bv_val = pwdc;
221 pi.pwd.bv_len = tmpint32;
223 Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",pi.uid.bv_val,0,0);
225 rc = pam_do_bind(ni, fp, op, &pi);
228 WRITE_INT32(fp,NSLCD_VERSION);
229 WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
230 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
231 WRITE_BERVAL(fp,&pi.uid);
232 WRITE_BERVAL(fp,&pi.dn);
234 WRITE_INT32(fp,pi.authz); /* authz */
235 WRITE_BERVAL(fp,&pi.msg); /* authzmsg */
239 static struct berval grpmsg =
240 BER_BVC("Access denied by group check");
241 static struct berval hostmsg =
242 BER_BVC("Access denied for this host");
243 static struct berval svcmsg =
244 BER_BVC("Access denied for this service");
245 static struct berval uidmsg =
246 BER_BVC("Access denied by UID check");
248 int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
250 struct berval dn, uid, svc, ruser, rhost, tty;
251 struct berval authzmsg = BER_BVNULL;
262 SlapReply rs = {REP_RESULT};
263 slap_callback cb = {0};
265 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
267 uid.bv_len = tmpint32;
268 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
270 dn.bv_len = tmpint32;
271 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
273 svc.bv_len = tmpint32;
274 READ_STRING_BUF2(fp,svcc,sizeof(ruserc));
275 ruser.bv_val = ruserc;
276 ruser.bv_len = tmpint32;
277 READ_STRING_BUF2(fp,svcc,sizeof(rhostc));
278 rhost.bv_val = rhostc;
279 rhost.bv_len = tmpint32;
280 READ_STRING_BUF2(fp,svcc,sizeof(ttyc));
282 tty.bv_len = tmpint32;
284 Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",dn.bv_val,0,0);
286 /* We don't do authorization if they weren't authenticated by us */
287 if (BER_BVISEMPTY(&dn)) {
288 rc = NSLCD_PAM_USER_UNKNOWN;
292 /* See if they have access to the host and service */
293 if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
294 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
295 struct berval hostdn = BER_BVNULL;
296 struct berval odn = op->o_ndn;
300 nssov_mapinfo *mi = &ni->ni_maps[NM_host];
302 struct berval filter = {sizeof(fbuf),fbuf};
303 SlapReply rs2 = {REP_RESULT};
305 /* Lookup the host entry */
306 nssov_filter_byname(mi,0,&global_host_bv,&filter);
307 cb.sc_private = &hostdn;
308 cb.sc_response = nssov_name2dn_cb;
309 op->o_callback = &cb;
310 op->o_req_dn = mi->mi_base;
311 op->o_req_ndn = mi->mi_base;
312 op->ors_scope = mi->mi_scope;
313 op->ors_filterstr = filter;
314 op->ors_filter = str2filter_x(op, filter.bv_val);
315 op->ors_attrs = slap_anlist_no_attrs;
316 op->ors_tlimit = SLAP_NO_LIMIT;
318 rc = op->o_bd->be_search(op, &rs2);
319 filter_free_x(op, op->ors_filter, 1);
321 if (BER_BVISEMPTY(&hostdn) &&
322 !BER_BVISEMPTY(&ni->ni_pam_defhost)) {
323 filter.bv_len = sizeof(fbuf);
324 filter.bv_val = fbuf;
325 memset(&rs2, 0, sizeof(rs2));
326 rs2.sr_type = REP_RESULT;
327 nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
328 op->ors_filterstr = filter;
329 op->ors_filter = str2filter_x(op, filter.bv_val);
330 rc = op->o_bd->be_search(op, &rs2);
331 filter_free_x(op, op->ors_filter, 1);
334 /* no host entry, no default host -> deny */
335 if (BER_BVISEMPTY(&hostdn)) {
336 rc = NSLCD_PAM_PERM_DENIED;
342 cb.sc_response = slap_null_cb;
343 cb.sc_private = NULL;
344 op->o_tag = LDAP_REQ_COMPARE;
345 op->o_req_dn = hostdn;
346 op->o_req_ndn = hostdn;
347 ava.aa_desc = nssov_pam_svc_ad;
350 rc = op->o_bd->be_compare( op, &rs );
351 if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
353 rc = NSLCD_PAM_PERM_DENIED;
360 /* See if they're a member of the group */
361 if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
362 !BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
363 ni->ni_pam_group_ad) {
364 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
365 op->o_callback = &cb;
366 cb.sc_response = slap_null_cb;
367 op->o_tag = LDAP_REQ_COMPARE;
368 op->o_req_dn = ni->ni_pam_group_dn;
369 op->o_req_ndn = ni->ni_pam_group_dn;
370 ava.aa_desc = ni->ni_pam_group_ad;
373 rc = op->o_bd->be_compare( op, &rs );
374 if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
376 rc = NSLCD_PAM_PERM_DENIED;
381 /* We need to check the user's entry for these bits */
382 if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
383 ni->ni_pam_template_ad ||
384 ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
385 rc = be_entry_get_rw( op, &dn, NULL, NULL, 0, &e );
386 if (rc != LDAP_SUCCESS) {
387 rc = NSLCD_PAM_USER_UNKNOWN;
391 if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
392 a = attr_find(e->e_attrs, nssov_pam_host_ad);
393 if (!a || attr_valfind( a,
394 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
395 SLAP_MR_VALUE_OF_SYNTAX,
396 &global_host_bv, NULL, op->o_tmpmemctx )) {
397 rc = NSLCD_PAM_PERM_DENIED;
402 if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
403 a = attr_find(e->e_attrs, nssov_pam_svc_ad);
404 if (!a || attr_valfind( a,
405 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
406 SLAP_MR_VALUE_OF_SYNTAX,
407 &svc, NULL, op->o_tmpmemctx )) {
408 rc = NSLCD_PAM_PERM_DENIED;
417 if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
420 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
421 a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
423 rc = NSLCD_PAM_PERM_DENIED;
427 id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
428 if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
429 rc = NSLCD_PAM_PERM_DENIED;
433 if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
434 (ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
435 rc = NSLCD_PAM_PERM_DENIED;
441 if (ni->ni_pam_template_ad) {
442 a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
445 else if (!BER_BVISEMPTY(&ni->ni_pam_template))
446 uid = ni->ni_pam_template;
448 rc = NSLCD_PAM_SUCCESS;
451 WRITE_INT32(fp,NSLCD_VERSION);
452 WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
453 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
454 WRITE_BERVAL(fp,&uid);
455 WRITE_BERVAL(fp,&dn);
457 WRITE_BERVAL(fp,&authzmsg);
459 be_entry_release_r(op, e);
464 static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
466 struct berval dn, uid, svc, tty, rhost, ruser;
474 slap_callback cb = {0};
475 SlapReply rs = {REP_RESULT};
476 char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
477 struct berval timestamp, bv[2], *nbv;
481 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
483 uid.bv_len = tmpint32;
484 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
486 dn.bv_len = tmpint32;
487 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
489 svc.bv_len = tmpint32;
490 READ_STRING_BUF2(fp,ttyc,sizeof(ttyc));
492 tty.bv_len = tmpint32;
493 READ_STRING_BUF2(fp,rhostc,sizeof(rhostc));
494 rhost.bv_val = rhostc;
495 rhost.bv_len = tmpint32;
496 READ_STRING_BUF2(fp,ruserc,sizeof(ruserc));
497 ruser.bv_val = ruserc;
498 ruser.bv_len = tmpint32;
499 READ_INT32(fp,stamp);
501 Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
502 action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', dn.bv_val,0);
504 if (!dn.bv_len || !ni->ni_pam_sessions) return 0;
508 for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
509 if (ni->ni_pam_sessions[i].bv_len != svc.bv_len)
511 if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, svc.bv_val)) {
516 if (!found) return 0;
519 slap_op_time( &op->o_time, &op->o_tincr );
520 timestamp.bv_len = sizeof(timebuf);
521 timestamp.bv_val = timebuf;
522 if (action == NSLCD_ACTION_PAM_SESS_O )
524 slap_timestamp( &stamp, ×tamp );
525 bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + svc.bv_len +
526 tty.bv_len + ruser.bv_len + rhost.bv_len + STRLENOF(" (@)");
527 bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
528 sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
529 timestamp.bv_val, global_host_bv.bv_val, svc.bv_val, tty.bv_val,
530 ruser.bv_val, rhost.bv_val);
535 attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
536 mod.sml_nvalues = nbv;
537 mod.sml_desc = ad_loginStatus;
538 mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
540 mod.sml_flags = SLAP_MOD_INTERNAL;
543 cb.sc_response = slap_null_cb;
544 op->o_callback = &cb;
545 op->o_tag = LDAP_REQ_MODIFY;
546 op->o_dn = op->o_bd->be_rootdn;
547 op->o_ndn = op->o_bd->be_rootndn;
548 op->orm_modlist = &mod;
549 op->orm_no_opattrs = 1;
552 op->o_bd->be_modify( op, &rs );
553 if ( mod.sml_next ) {
554 slap_mods_free( mod.sml_next, 1 );
556 ber_bvarray_free_x( nbv, op->o_tmpmemctx );
558 WRITE_INT32(fp,NSLCD_VERSION);
559 WRITE_INT32(fp,action);
560 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
561 WRITE_INT32(fp,op->o_time);
565 int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
567 return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
570 int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
572 return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
575 int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op)
587 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
588 pi.uid.bv_val = uidc;
589 pi.uid.bv_len = tmpint32;
590 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
592 pi.dn.bv_len = tmpint32;
593 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
594 pi.svc.bv_val = svcc;
595 pi.svc.bv_len = tmpint32;
596 READ_STRING_BUF2(fp,opwc,sizeof(opwc));
597 pi.pwd.bv_val = opwc;
598 pi.pwd.bv_len = tmpint32;
599 READ_STRING_BUF2(fp,npwc,sizeof(npwc));
601 npw.bv_len = tmpint32;
603 Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s\n",
604 pi.dn.bv_val,pi.uid.bv_val,0);
608 /* This is a prelim check */
609 if (BER_BVISEMPTY(&pi.dn)) {
610 rc = pam_do_bind(ni,fp,op,&pi);
611 if (rc == NSLCD_PAM_IGNORE)
612 rc = NSLCD_PAM_SUCCESS;
614 BerElementBuffer berbuf;
615 BerElement *ber = (BerElement *)&berbuf;
617 SlapReply rs = {REP_RESULT};
618 slap_callback cb = {0};
620 ber_init_w_nullc(ber, LBER_USE_DER);
621 ber_printf(ber, "{");
622 if (!BER_BVISEMPTY(&pi.pwd))
623 ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
625 if (!BER_BVISEMPTY(&npw))
626 ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
628 ber_printf(ber, "N}");
629 ber_flatten2(ber, &bv, 0);
630 op->o_tag = LDAP_REQ_EXTENDED;
631 op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
632 op->ore_reqdata = &bv;
635 op->o_callback = &cb;
636 op->o_conn->c_authz_backend = op->o_bd;
637 cb.sc_response = slap_null_cb;
638 op->o_bd = frontendDB;
639 rc = op->o_bd->be_extended(op, &rs);
641 ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
642 if (rc == LDAP_SUCCESS)
643 rc = NSLCD_PAM_SUCCESS;
645 rc = NSLCD_PAM_PERM_DENIED;
647 WRITE_INT32(fp,NSLCD_VERSION);
648 WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
649 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
650 WRITE_BERVAL(fp,&pi.uid);
651 WRITE_BERVAL(fp,&pi.dn);
653 WRITE_BERVAL(fp,&pi.msg);
662 code = slap_str2ad( "loginStatus", &ad_loginStatus, &text );