1 /* pam.c - pam processing routines */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2008-2009 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;
24 const char *at_loginStatus =
25 "( 1.3.6.1.4.1.4745.1.20.1 "
26 "NAME ( 'loginStatus' ) "
27 "DESC 'Currently logged in sessions for a user' "
28 "EQUALITY caseIgnoreMatch "
29 "SUBSTR caseIgnoreSubstringsMatch "
30 "ORDERING caseIgnoreOrderingMatch "
31 "SYNTAX OMsDirectoryString "
32 "USAGE directoryOperation )";
43 static int pam_bindcb(
44 Operation *op, SlapReply *rs)
46 struct paminfo *pi = op->o_callback->sc_private;
47 LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
51 ber_int_t expire, grace;
52 LDAPPasswordPolicyError error;
56 int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
57 &expire,&grace,&error);
58 if (rc == LDAP_SUCCESS) {
60 char *unit = "seconds";
73 #if 0 /* Who warns about expiration so far in advance? */
87 pi->msg.bv_len = sprintf(pi->msg.bv_val,
88 "\nWARNING: Password expires in %d %s\n", expire, unit);
89 } else if (grace > 0) {
90 pi->msg.bv_len = sprintf(pi->msg.bv_val,
91 "Password expired; %d grace logins remaining",
93 pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
94 } else if (error != PP_noError) {
95 ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
98 case PP_passwordExpired:
99 /* report this during authz */
100 rs->sr_err = LDAP_SUCCESS;
102 case PP_changeAfterReset:
103 pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
107 ldap_ld_free(ld,0,NULL,NULL);
113 int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
117 slap_callback cb = {0};
118 SlapReply rs = {REP_RESULT};
121 pi->msg.bv_val = pi->pwd.bv_val;
123 pi->authz = NSLCD_PAM_SUCCESS;
126 if (!isvalidusername(&pi->uid)) {
127 Debug(LDAP_DEBUG_ANY,"nssov_pam_do_bind(%s): invalid user name\n",
129 rc = NSLCD_PAM_USER_UNKNOWN;
133 if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
134 int hlen = global_host_bv.bv_len;
136 /* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
137 sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
138 STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
139 sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
140 sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
141 pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
142 slap_sasl2dn(op, &sdn, &pi->dn, 0);
143 op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
146 /* If no luck, do a basic uid search */
147 if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
148 nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
149 if (!BER_BVISEMPTY(&pi->dn)) {
151 dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
155 if (BER_BVISEMPTY(&pi->dn)) {
156 rc = NSLCD_PAM_USER_UNKNOWN;
160 if (BER_BVISEMPTY(&pi->pwd)) {
161 rc = NSLCD_PAM_IGNORE;
165 /* Should only need to do this once at open time, but there's always
166 * the possibility that ppolicy will get loaded later.
169 rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
172 /* of course, 0 is a valid cid, but it won't be ppolicy... */
174 op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
176 cb.sc_response = pam_bindcb;
178 op->o_callback = &cb;
179 op->o_dn.bv_val[0] = 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.
195 if (rc == LDAP_SUCCESS)
196 send_ldap_result(op, &rs);
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;
206 int pam_authc(nssov_info *ni,TFILE *fp,Operation *op)
210 slap_callback cb = {0};
211 SlapReply rs = {REP_RESULT};
216 struct berval sdn, dn;
220 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
221 pi.uid.bv_val = uidc;
222 pi.uid.bv_len = tmpint32;
223 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
225 pi.dn.bv_len = tmpint32;
226 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
227 pi.svc.bv_val = svcc;
228 pi.svc.bv_len = tmpint32;
229 READ_STRING_BUF2(fp,pwdc,sizeof(pwdc));
230 pi.pwd.bv_val = pwdc;
231 pi.pwd.bv_len = tmpint32;
233 Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",pi.uid.bv_val,0,0);
235 rc = pam_do_bind(ni, fp, op, &pi);
238 WRITE_INT32(fp,NSLCD_VERSION);
239 WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
240 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
241 WRITE_BERVAL(fp,&pi.uid);
242 WRITE_BERVAL(fp,&pi.dn);
244 WRITE_INT32(fp,pi.authz); /* authz */
245 WRITE_BERVAL(fp,&pi.msg); /* authzmsg */
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");
258 int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
260 struct berval dn, uid, svc, ruser, rhost, tty;
261 struct berval authzmsg = BER_BVNULL;
272 SlapReply rs = {REP_RESULT};
273 slap_callback cb = {0};
275 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
277 uid.bv_len = tmpint32;
278 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
280 dn.bv_len = tmpint32;
281 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
283 svc.bv_len = tmpint32;
284 READ_STRING_BUF2(fp,svcc,sizeof(ruserc));
285 ruser.bv_val = ruserc;
286 ruser.bv_len = tmpint32;
287 READ_STRING_BUF2(fp,svcc,sizeof(rhostc));
288 rhost.bv_val = rhostc;
289 rhost.bv_len = tmpint32;
290 READ_STRING_BUF2(fp,svcc,sizeof(ttyc));
292 tty.bv_len = tmpint32;
294 Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",dn.bv_val,0,0);
296 /* We don't do authorization if they weren't authenticated by us */
297 if (BER_BVISEMPTY(&dn)) {
298 rc = NSLCD_PAM_USER_UNKNOWN;
302 /* See if they have access to the host and service */
303 if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
304 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
305 struct berval hostdn = BER_BVNULL;
306 struct berval odn = op->o_ndn;
310 nssov_mapinfo *mi = &ni->ni_maps[NM_host];
312 struct berval filter = {sizeof(fbuf),fbuf};
313 SlapReply rs2 = {REP_RESULT};
315 /* Lookup the host entry */
316 nssov_filter_byname(mi,0,&global_host_bv,&filter);
317 cb.sc_private = &hostdn;
318 cb.sc_response = nssov_name2dn_cb;
319 op->o_callback = &cb;
320 op->o_req_dn = mi->mi_base;
321 op->o_req_ndn = mi->mi_base;
322 op->ors_scope = mi->mi_scope;
323 op->ors_filterstr = filter;
324 op->ors_filter = str2filter_x(op, filter.bv_val);
325 op->ors_attrs = slap_anlist_no_attrs;
326 op->ors_tlimit = SLAP_NO_LIMIT;
328 rc = op->o_bd->be_search(op, &rs2);
329 filter_free_x(op, op->ors_filter, 1);
331 if (BER_BVISEMPTY(&hostdn) &&
332 !BER_BVISEMPTY(&ni->ni_pam_defhost)) {
333 filter.bv_len = sizeof(fbuf);
334 filter.bv_val = fbuf;
335 memset(&rs2, 0, sizeof(rs2));
336 rs2.sr_type = REP_RESULT;
337 nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
338 op->ors_filterstr = filter;
339 op->ors_filter = str2filter_x(op, filter.bv_val);
340 rc = op->o_bd->be_search(op, &rs2);
341 filter_free_x(op, op->ors_filter, 1);
344 /* no host entry, no default host -> deny */
345 if (BER_BVISEMPTY(&hostdn)) {
346 rc = NSLCD_PAM_PERM_DENIED;
352 cb.sc_response = slap_null_cb;
353 cb.sc_private = NULL;
354 op->o_tag = LDAP_REQ_COMPARE;
355 op->o_req_dn = hostdn;
356 op->o_req_ndn = hostdn;
357 ava.aa_desc = nssov_pam_svc_ad;
360 rc = op->o_bd->be_compare( op, &rs );
361 if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
363 rc = NSLCD_PAM_PERM_DENIED;
370 /* See if they're a member of the group */
371 if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
372 !BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
373 ni->ni_pam_group_ad) {
374 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
375 op->o_callback = &cb;
376 cb.sc_response = slap_null_cb;
377 op->o_tag = LDAP_REQ_COMPARE;
378 op->o_req_dn = ni->ni_pam_group_dn;
379 op->o_req_ndn = ni->ni_pam_group_dn;
380 ava.aa_desc = ni->ni_pam_group_ad;
383 rc = op->o_bd->be_compare( op, &rs );
384 if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
386 rc = NSLCD_PAM_PERM_DENIED;
391 /* We need to check the user's entry for these bits */
392 if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
393 ni->ni_pam_template_ad ||
394 ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
395 rc = be_entry_get_rw( op, &dn, NULL, NULL, 0, &e );
396 if (rc != LDAP_SUCCESS) {
397 rc = NSLCD_PAM_USER_UNKNOWN;
401 if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
402 a = attr_find(e->e_attrs, nssov_pam_host_ad);
403 if (!a || attr_valfind( a,
404 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
405 SLAP_MR_VALUE_OF_SYNTAX,
406 &global_host_bv, NULL, op->o_tmpmemctx )) {
407 rc = NSLCD_PAM_PERM_DENIED;
412 if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
413 a = attr_find(e->e_attrs, nssov_pam_svc_ad);
414 if (!a || attr_valfind( a,
415 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
416 SLAP_MR_VALUE_OF_SYNTAX,
417 &svc, NULL, op->o_tmpmemctx )) {
418 rc = NSLCD_PAM_PERM_DENIED;
427 if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
430 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
431 a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
433 rc = NSLCD_PAM_PERM_DENIED;
437 id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
438 if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
439 rc = NSLCD_PAM_PERM_DENIED;
443 if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
444 (ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
445 rc = NSLCD_PAM_PERM_DENIED;
451 if (ni->ni_pam_template_ad) {
452 a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
455 else if (!BER_BVISEMPTY(&ni->ni_pam_template))
456 uid = ni->ni_pam_template;
458 rc = NSLCD_PAM_SUCCESS;
461 WRITE_INT32(fp,NSLCD_VERSION);
462 WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
463 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
464 WRITE_BERVAL(fp,&uid);
465 WRITE_BERVAL(fp,&dn);
467 WRITE_BERVAL(fp,&authzmsg);
469 be_entry_release_r(op, e);
474 static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
476 struct berval dn, uid, svc, tty, rhost, ruser;
484 slap_callback cb = {0};
485 SlapReply rs = {REP_RESULT};
486 char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
487 struct berval timestamp, bv[2], *nbv;
491 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
493 uid.bv_len = tmpint32;
494 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
496 dn.bv_len = tmpint32;
497 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
499 svc.bv_len = tmpint32;
500 READ_STRING_BUF2(fp,ttyc,sizeof(ttyc));
502 tty.bv_len = tmpint32;
503 READ_STRING_BUF2(fp,rhostc,sizeof(rhostc));
504 rhost.bv_val = rhostc;
505 rhost.bv_len = tmpint32;
506 READ_STRING_BUF2(fp,ruserc,sizeof(ruserc));
507 ruser.bv_val = ruserc;
508 ruser.bv_len = tmpint32;
509 READ_INT32(fp,stamp);
511 Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
512 action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', dn.bv_val,0);
514 if (!dn.bv_len || !ni->ni_pam_sessions) return 0;
518 for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
519 if (ni->ni_pam_sessions[i].bv_len != svc.bv_len)
521 if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, svc.bv_val)) {
526 if (!found) return 0;
529 slap_op_time( &op->o_time, &op->o_tincr );
530 timestamp.bv_len = sizeof(timebuf);
531 timestamp.bv_val = timebuf;
532 if (action == NSLCD_ACTION_PAM_SESS_O )
534 slap_timestamp( &stamp, ×tamp );
535 bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + svc.bv_len +
536 tty.bv_len + ruser.bv_len + rhost.bv_len + STRLENOF(" (@)");
537 bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
538 sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
539 timestamp.bv_val, global_host_bv.bv_val, svc.bv_val, tty.bv_val,
540 ruser.bv_val, rhost.bv_val);
545 attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
546 mod.sml_nvalues = nbv;
547 mod.sml_desc = ad_loginStatus;
548 mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
550 mod.sml_flags = SLAP_MOD_INTERNAL;
553 cb.sc_response = slap_null_cb;
554 op->o_callback = &cb;
555 op->o_tag = LDAP_REQ_MODIFY;
556 op->o_dn = op->o_bd->be_rootdn;
557 op->o_ndn = op->o_bd->be_rootndn;
558 op->orm_modlist = &mod;
559 op->orm_no_opattrs = 1;
562 op->o_bd->be_modify( op, &rs );
563 if ( mod.sml_next ) {
564 slap_mods_free( mod.sml_next, 1 );
566 ber_bvarray_free_x( nbv, op->o_tmpmemctx );
568 WRITE_INT32(fp,NSLCD_VERSION);
569 WRITE_INT32(fp,action);
570 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
571 WRITE_INT32(fp,op->o_time);
575 int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
577 return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
580 int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
582 return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
585 int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op)
597 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
598 pi.uid.bv_val = uidc;
599 pi.uid.bv_len = tmpint32;
600 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
602 pi.dn.bv_len = tmpint32;
603 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
604 pi.svc.bv_val = svcc;
605 pi.svc.bv_len = tmpint32;
606 READ_STRING_BUF2(fp,opwc,sizeof(opwc));
607 pi.pwd.bv_val = opwc;
608 pi.pwd.bv_len = tmpint32;
609 READ_STRING_BUF2(fp,npwc,sizeof(npwc));
611 npw.bv_len = tmpint32;
613 Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s\n",
614 pi.dn.bv_val,pi.uid.bv_val,0);
618 /* This is a prelim check */
619 if (BER_BVISEMPTY(&pi.dn)) {
620 rc = pam_do_bind(ni,fp,op,&pi);
621 if (rc == NSLCD_PAM_IGNORE)
622 rc = NSLCD_PAM_SUCCESS;
624 BerElementBuffer berbuf;
625 BerElement *ber = (BerElement *)&berbuf;
627 SlapReply rs = {REP_RESULT};
628 slap_callback cb = {0};
630 ber_init_w_nullc(ber, LBER_USE_DER);
631 ber_printf(ber, "{");
632 if (!BER_BVISEMPTY(&pi.pwd))
633 ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
635 if (!BER_BVISEMPTY(&npw))
636 ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
638 ber_printf(ber, "N}");
639 ber_flatten2(ber, &bv, 0);
640 op->o_tag = LDAP_REQ_EXTENDED;
641 op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
642 op->ore_reqdata = &bv;
645 op->o_callback = &cb;
646 op->o_conn->c_authz_backend = op->o_bd;
647 cb.sc_response = slap_null_cb;
648 op->o_bd = frontendDB;
649 rc = op->o_bd->be_extended(op, &rs);
651 ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
652 if (rc == LDAP_SUCCESS)
653 rc = NSLCD_PAM_SUCCESS;
655 rc = NSLCD_PAM_PERM_DENIED;
657 WRITE_INT32(fp,NSLCD_VERSION);
658 WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
659 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
660 WRITE_BERVAL(fp,&pi.uid);
661 WRITE_BERVAL(fp,&pi.dn);
663 WRITE_BERVAL(fp,&pi.msg);
671 code = register_at( at_loginStatus, &ad_loginStatus, 0 );