1 /* pam.c - pam processing routines */
4 * Copyright 2009 by Howard Chu, Symas Corp.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
19 static int ppolicy_cid;
20 static AttributeDescription *ad_loginStatus;
22 const char *at_loginStatus =
23 "( 1.3.6.1.4.1.4745.1.20.1 "
24 "NAME ( 'loginStatus' ) "
25 "DESC 'Currently logged in sessions for a user' "
26 "EQUALITY caseIgnoreMatch "
27 "SUBSTR caseIgnoreSubstringsMatch "
28 "ORDERING caseIgnoreOrderingMatch "
29 "SYNTAX OMsDirectoryString "
30 "USAGE directoryOperation )";
41 static int pam_bindcb(
42 Operation *op, SlapReply *rs)
44 struct paminfo *pi = op->o_callback->sc_private;
45 LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
49 ber_int_t expire, grace;
50 LDAPPasswordPolicyError error;
54 int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
55 &expire,&grace,&error);
56 if (rc == LDAP_SUCCESS) {
58 char *unit = "seconds";
71 #if 0 /* Who warns about expiration so far in advance? */
85 pi->msg.bv_len = sprintf(pi->msg.bv_val,
86 "\nWARNING: Password expires in %d %s\n", expire, unit);
87 } else if (grace > 0) {
88 pi->msg.bv_len = sprintf(pi->msg.bv_val,
89 "Password expired; %d grace logins remaining",
91 pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
92 } else if (error != PP_noError) {
93 ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
96 case PP_passwordExpired:
97 /* report this during authz */
98 rs->sr_err = LDAP_SUCCESS;
100 case PP_changeAfterReset:
101 pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
105 ldap_ld_free(ld,0,NULL,NULL);
111 int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
115 slap_callback cb = {0};
116 SlapReply rs = {REP_RESULT};
119 pi->msg.bv_val = pi->pwd.bv_val;
121 pi->authz = NSLCD_PAM_SUCCESS;
124 if (!isvalidusername(&pi->uid)) {
125 Debug(LDAP_DEBUG_ANY,"nssov_pam_do_bind(%s): invalid user name\n",
127 rc = NSLCD_PAM_USER_UNKNOWN;
131 if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
132 int hlen = global_host_bv.bv_len;
134 /* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
135 sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
136 STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
137 sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
138 sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
139 pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
140 slap_sasl2dn(op, &sdn, &pi->dn, 0);
141 op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
144 /* If no luck, do a basic uid search */
145 if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
146 nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
147 if (!BER_BVISEMPTY(&pi->dn)) {
149 dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
153 if (BER_BVISEMPTY(&pi->dn)) {
154 rc = NSLCD_PAM_USER_UNKNOWN;
158 if (BER_BVISEMPTY(&pi->pwd)) {
159 rc = NSLCD_PAM_IGNORE;
163 /* Should only need to do this once at open time, but there's always
164 * the possibility that ppolicy will get loaded later.
167 rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
170 /* of course, 0 is a valid cid, but it won't be ppolicy... */
172 op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
174 cb.sc_response = pam_bindcb;
176 op->o_callback = &cb;
177 op->o_dn.bv_val[0] = 0;
179 op->o_ndn.bv_val[0] = 0;
180 op->o_ndn.bv_len = 0;
181 op->o_tag = LDAP_REQ_BIND;
182 op->o_protocol = LDAP_VERSION3;
183 op->orb_method = LDAP_AUTH_SIMPLE;
184 op->orb_cred = pi->pwd;
185 op->o_req_dn = pi->dn;
186 op->o_req_ndn = pi->dn;
187 slap_op_time( &op->o_time, &op->o_tincr );
188 rc = op->o_bd->be_bind( op, &rs );
189 memset(pi->pwd.bv_val,0,pi->pwd.bv_len);
190 /* quirk: on successful bind, caller has to send result. we need
191 * to make sure callbacks run.
193 if (rc == LDAP_SUCCESS)
194 send_ldap_result(op, &rs);
196 case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
197 case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
198 default: rc = NSLCD_PAM_AUTH_ERR; break;
204 int pam_authc(nssov_info *ni,TFILE *fp,Operation *op)
208 slap_callback cb = {0};
209 SlapReply rs = {REP_RESULT};
214 struct berval sdn, dn;
218 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
219 pi.uid.bv_val = uidc;
220 pi.uid.bv_len = tmpint32;
221 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
223 pi.dn.bv_len = tmpint32;
224 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
225 pi.svc.bv_val = svcc;
226 pi.svc.bv_len = tmpint32;
227 READ_STRING_BUF2(fp,pwdc,sizeof(pwdc));
228 pi.pwd.bv_val = pwdc;
229 pi.pwd.bv_len = tmpint32;
231 Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",pi.uid.bv_val,0,0);
233 rc = pam_do_bind(ni, fp, op, &pi);
236 WRITE_INT32(fp,NSLCD_VERSION);
237 WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
238 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
239 WRITE_BERVAL(fp,&pi.uid);
240 WRITE_BERVAL(fp,&pi.dn);
242 WRITE_INT32(fp,pi.authz); /* authz */
243 WRITE_BERVAL(fp,&pi.msg); /* authzmsg */
247 static struct berval grpmsg =
248 BER_BVC("Access denied by group check");
249 static struct berval hostmsg =
250 BER_BVC("Access denied for this host");
251 static struct berval svcmsg =
252 BER_BVC("Access denied for this service");
253 static struct berval uidmsg =
254 BER_BVC("Access denied by UID check");
256 int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
258 struct berval dn, uid, svc, ruser, rhost, tty;
259 struct berval authzmsg = BER_BVNULL;
267 int rc = NSLCD_PAM_SUCCESS;
270 SlapReply rs = {REP_RESULT};
271 slap_callback cb = {0};
273 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
275 uid.bv_len = tmpint32;
276 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
278 dn.bv_len = tmpint32;
279 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
281 svc.bv_len = tmpint32;
282 READ_STRING_BUF2(fp,svcc,sizeof(ruserc));
283 ruser.bv_val = ruserc;
284 ruser.bv_len = tmpint32;
285 READ_STRING_BUF2(fp,svcc,sizeof(rhostc));
286 rhost.bv_val = rhostc;
287 rhost.bv_len = tmpint32;
288 READ_STRING_BUF2(fp,svcc,sizeof(ttyc));
290 tty.bv_len = tmpint32;
292 Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",dn.bv_val,0,0);
294 /* We don't do authorization if they weren't authenticated by us */
295 if (BER_BVISEMPTY(&dn)) {
296 rc = NSLCD_PAM_USER_UNKNOWN;
300 /* See if they have access to the host and service */
301 if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
302 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
303 struct berval hostdn = BER_BVNULL;
304 struct berval odn = op->o_ndn;
308 nssov_mapinfo *mi = &ni->ni_maps[NM_host];
310 struct berval filter = {sizeof(fbuf),fbuf};
311 SlapReply rs2 = {REP_RESULT};
313 /* Lookup the host entry */
314 nssov_filter_byname(mi,0,&global_host_bv,&filter);
315 cb.sc_private = &hostdn;
316 cb.sc_response = nssov_name2dn_cb;
317 op->o_callback = &cb;
318 op->o_req_dn = mi->mi_base;
319 op->o_req_ndn = mi->mi_base;
320 op->ors_scope = mi->mi_scope;
321 op->ors_filterstr = filter;
322 op->ors_filter = str2filter_x(op, filter.bv_val);
323 op->ors_attrs = slap_anlist_no_attrs;
324 op->ors_tlimit = SLAP_NO_LIMIT;
326 rc = op->o_bd->be_search(op, &rs2);
327 filter_free_x(op, op->ors_filter, 1);
329 if (BER_BVISEMPTY(&hostdn) &&
330 !BER_BVISEMPTY(&ni->ni_pam_defhost)) {
331 filter.bv_len = sizeof(fbuf);
332 filter.bv_val = fbuf;
333 memset(&rs2, 0, sizeof(rs2));
334 rs2.sr_type = REP_RESULT;
335 nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
336 op->ors_filterstr = filter;
337 op->ors_filter = str2filter_x(op, filter.bv_val);
338 rc = op->o_bd->be_search(op, &rs2);
339 filter_free_x(op, op->ors_filter, 1);
342 /* no host entry, no default host -> deny */
343 if (BER_BVISEMPTY(&hostdn)) {
344 rc = NSLCD_PAM_PERM_DENIED;
350 cb.sc_response = slap_null_cb;
351 cb.sc_private = NULL;
352 op->o_tag = LDAP_REQ_COMPARE;
353 op->o_req_dn = hostdn;
354 op->o_req_ndn = hostdn;
355 ava.aa_desc = nssov_pam_svc_ad;
358 rc = op->o_bd->be_compare( op, &rs );
359 if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
361 rc = NSLCD_PAM_PERM_DENIED;
368 /* See if they're a member of the group */
369 if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
370 !BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
371 ni->ni_pam_group_ad) {
372 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
373 op->o_callback = &cb;
374 cb.sc_response = slap_null_cb;
375 op->o_tag = LDAP_REQ_COMPARE;
376 op->o_req_dn = ni->ni_pam_group_dn;
377 op->o_req_ndn = ni->ni_pam_group_dn;
378 ava.aa_desc = ni->ni_pam_group_ad;
381 rc = op->o_bd->be_compare( op, &rs );
382 if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
384 rc = NSLCD_PAM_PERM_DENIED;
389 /* We need to check the user's entry for these bits */
390 if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
391 ni->ni_pam_template_ad ||
392 ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
393 rc = be_entry_get_rw( op, &dn, NULL, NULL, 0, &e );
394 if (rc != LDAP_SUCCESS) {
395 rc = NSLCD_PAM_USER_UNKNOWN;
399 if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
400 a = attr_find(e->e_attrs, nssov_pam_host_ad);
401 if (!a || value_find_ex( nssov_pam_host_ad,
402 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
403 a->a_vals, &global_host_bv, op->o_tmpmemctx )) {
404 rc = NSLCD_PAM_PERM_DENIED;
409 if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
410 a = attr_find(e->e_attrs, nssov_pam_svc_ad);
411 if (!a || value_find_ex( nssov_pam_svc_ad,
412 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
413 a->a_vals, &svc, op->o_tmpmemctx )) {
414 rc = NSLCD_PAM_PERM_DENIED;
423 if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
426 nssov_mapinfo *mi = &ni->ni_maps[NM_host];
427 a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
429 rc = NSLCD_PAM_PERM_DENIED;
433 id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
434 if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
435 rc = NSLCD_PAM_PERM_DENIED;
439 if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
440 (ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
441 rc = NSLCD_PAM_PERM_DENIED;
447 if (ni->ni_pam_template_ad) {
448 a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
451 else if (!BER_BVISEMPTY(&ni->ni_pam_template))
452 uid = ni->ni_pam_template;
456 WRITE_INT32(fp,NSLCD_VERSION);
457 WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
458 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
459 WRITE_BERVAL(fp,&uid);
460 WRITE_BERVAL(fp,&dn);
462 WRITE_BERVAL(fp,&authzmsg);
464 be_entry_release_r(op, e);
469 static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
471 struct berval dn, uid, svc, tty, rhost, ruser;
479 slap_callback cb = {0};
480 SlapReply rs = {REP_RESULT};
481 char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
482 struct berval timestamp, bv[2], *nbv;
486 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
488 uid.bv_len = tmpint32;
489 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
491 dn.bv_len = tmpint32;
492 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
494 svc.bv_len = tmpint32;
495 READ_STRING_BUF2(fp,ttyc,sizeof(ttyc));
497 tty.bv_len = tmpint32;
498 READ_STRING_BUF2(fp,rhostc,sizeof(rhostc));
499 rhost.bv_val = rhostc;
500 rhost.bv_len = tmpint32;
501 READ_STRING_BUF2(fp,ruserc,sizeof(ruserc));
502 ruser.bv_val = ruserc;
503 ruser.bv_len = tmpint32;
504 READ_INT32(fp,stamp);
506 Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
507 action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', dn.bv_val,0);
509 if (!dn.bv_len || !ni->ni_pam_sessions) return 0;
513 for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
514 if (ni->ni_pam_sessions[i].bv_len != svc.bv_len)
516 if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, svc.bv_val)) {
521 if (!found) return 0;
524 slap_op_time( &op->o_time, &op->o_tincr );
525 timestamp.bv_len = sizeof(timebuf);
526 timestamp.bv_val = timebuf;
527 if (action == NSLCD_ACTION_PAM_SESS_O )
529 slap_timestamp( &stamp, ×tamp );
530 bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + svc.bv_len +
531 tty.bv_len + ruser.bv_len + rhost.bv_len + STRLENOF(" (@)");
532 bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
533 sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
534 timestamp.bv_val, global_host_bv.bv_val, svc.bv_val, tty.bv_val,
535 ruser.bv_val, rhost.bv_val);
540 attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
541 mod.sml_nvalues = nbv;
542 mod.sml_desc = ad_loginStatus;
543 mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
545 mod.sml_flags = SLAP_MOD_INTERNAL;
548 cb.sc_response = slap_null_cb;
549 op->o_callback = &cb;
550 op->o_tag = LDAP_REQ_MODIFY;
551 op->o_dn = op->o_bd->be_rootdn;
552 op->o_ndn = op->o_bd->be_rootndn;
553 op->orm_modlist = &mod;
554 op->orm_no_opattrs = 1;
557 op->o_bd->be_modify( op, &rs );
558 if ( mod.sml_next ) {
559 slap_mods_free( mod.sml_next, 1 );
561 ber_bvarray_free_x( nbv, op->o_tmpmemctx );
563 WRITE_INT32(fp,NSLCD_VERSION);
564 WRITE_INT32(fp,action);
565 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
566 WRITE_INT32(fp,op->o_time);
570 int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
572 return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
575 int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
577 return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
580 int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op)
592 READ_STRING_BUF2(fp,uidc,sizeof(uidc));
593 pi.uid.bv_val = uidc;
594 pi.uid.bv_len = tmpint32;
595 READ_STRING_BUF2(fp,dnc,sizeof(dnc));
597 pi.dn.bv_len = tmpint32;
598 READ_STRING_BUF2(fp,svcc,sizeof(svcc));
599 pi.svc.bv_val = svcc;
600 pi.svc.bv_len = tmpint32;
601 READ_STRING_BUF2(fp,opwc,sizeof(opwc));
602 pi.pwd.bv_val = opwc;
603 pi.pwd.bv_len = tmpint32;
604 READ_STRING_BUF2(fp,npwc,sizeof(npwc));
606 npw.bv_len = tmpint32;
608 Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s\n",
609 pi.dn.bv_val,pi.uid.bv_val,0);
613 /* This is a prelim check */
614 if (BER_BVISEMPTY(&pi.dn)) {
615 rc = pam_do_bind(ni,fp,op,&pi);
616 if (rc == NSLCD_PAM_IGNORE)
617 rc = NSLCD_PAM_SUCCESS;
619 BerElementBuffer berbuf;
620 BerElement *ber = (BerElement *)&berbuf;
622 SlapReply rs = {REP_RESULT};
623 slap_callback cb = {0};
625 ber_init_w_nullc(ber, LBER_USE_DER);
626 ber_printf(ber, "{");
627 if (!BER_BVISEMPTY(&pi.pwd))
628 ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
630 if (!BER_BVISEMPTY(&npw))
631 ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
633 ber_printf(ber, "N}");
634 ber_flatten2(ber, &bv, 0);
635 op->o_tag = LDAP_REQ_EXTENDED;
636 op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
637 op->ore_reqdata = &bv;
640 op->o_callback = &cb;
641 op->o_conn->c_authz_backend = op->o_bd;
642 cb.sc_response = slap_null_cb;
643 op->o_bd = frontendDB;
644 rc = op->o_bd->be_extended(op, &rs);
646 ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
647 if (rc == LDAP_SUCCESS)
648 rc = NSLCD_PAM_SUCCESS;
650 rc = NSLCD_PAM_PERM_DENIED;
652 WRITE_INT32(fp,NSLCD_VERSION);
653 WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
654 WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
655 WRITE_BERVAL(fp,&pi.uid);
656 WRITE_BERVAL(fp,&pi.dn);
658 WRITE_BERVAL(fp,&pi.msg);
666 code = register_at( at_loginStatus, &ad_loginStatus, 0 );