summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/nssov/pam.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
commit5ea77a75dd2d2158401331879f3c8f47940a732c (patch)
treed89dc06e9f4850a900f161e25f84e922c4f86cc8 /contrib/slapd-modules/nssov/pam.c
parentInitial commit. (diff)
downloadopenldap-upstream/2.5.13+dfsg.tar.xz
openldap-upstream/2.5.13+dfsg.zip
Adding upstream version 2.5.13+dfsg.upstream/2.5.13+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/slapd-modules/nssov/pam.c')
-rw-r--r--contrib/slapd-modules/nssov/pam.c862
1 files changed, 862 insertions, 0 deletions
diff --git a/contrib/slapd-modules/nssov/pam.c b/contrib/slapd-modules/nssov/pam.c
new file mode 100644
index 0000000..1d416c7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/pam.c
@@ -0,0 +1,862 @@
+/* pam.c - pam processing routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * Portions Copyright 2013 by Ted C. Cheng, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "nssov.h"
+#include "lutil.h"
+
+#undef ldap_debug /* silence a warning in ldap-int.h */
+#include "../../../libraries/libldap/ldap-int.h" /* for ldap_ld_free */
+
+static int ppolicy_cid;
+static AttributeDescription *ad_loginStatus;
+
+struct paminfo {
+ struct berval uid;
+ struct berval dn;
+ struct berval svc;
+ struct berval ruser;
+ struct berval rhost;
+ struct berval tty;
+ struct berval pwd;
+ int authz;
+ struct berval msg;
+ int ispwdmgr;
+};
+
+static int pam_bindcb(
+ Operation *op, SlapReply *rs)
+{
+ struct paminfo *pi = op->o_callback->sc_private;
+ LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
+ rs->sr_ctrls, NULL);
+ if (ctrl) {
+ LDAP *ld;
+ ber_int_t expire, grace;
+ LDAPPasswordPolicyError error;
+
+ ldap_create(&ld);
+ if (ld) {
+ int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
+ &expire,&grace,&error);
+ if (rc == LDAP_SUCCESS) {
+ if (expire >= 0) {
+ char *unit = "seconds";
+ if (expire > 60) {
+ expire /= 60;
+ unit = "minutes";
+ }
+ if (expire > 60) {
+ expire /= 60;
+ unit = "hours";
+ }
+ if (expire > 24) {
+ expire /= 24;
+ unit = "days";
+ }
+#if 0 /* Who warns about expiration so far in advance? */
+ if (expire > 7) {
+ expire /= 7;
+ unit = "weeks";
+ }
+ if (expire > 4) {
+ expire /= 4;
+ unit = "months";
+ }
+ if (expire > 12) {
+ expire /= 12;
+ unit = "years";
+ }
+#endif
+ pi->msg.bv_len = sprintf(pi->msg.bv_val,
+ "\nWARNING: Password expires in %d %s\n", expire, unit);
+ } else if (grace > 0) {
+ pi->msg.bv_len = sprintf(pi->msg.bv_val,
+ "Password expired; %d grace logins remaining",
+ grace);
+ pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+ } else if (error != PP_noError) {
+ ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
+ &pi->msg);
+ switch (error) {
+ case PP_passwordExpired:
+ /* report this during authz */
+ rs->sr_err = LDAP_SUCCESS;
+ /* fallthru */
+ case PP_changeAfterReset:
+ pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+ }
+ }
+ }
+ ldap_ld_free(ld,0,NULL,NULL);
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int pam_uid2dn(nssov_info *ni, Operation *op,
+ struct paminfo *pi)
+{
+ struct berval sdn;
+
+ BER_BVZERO(&pi->dn);
+
+ if (!isvalidusername(&pi->uid)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_pam_uid2dn(%s): invalid user name\n",
+ pi->uid.bv_val ? pi->uid.bv_val : "NULL" );
+ return NSLCD_PAM_USER_UNKNOWN;
+ }
+
+ if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
+ int hlen = global_host_bv.bv_len;
+
+ /* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
+ sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
+ STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
+ sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
+ sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
+ pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
+ slap_sasl2dn(op, &sdn, &pi->dn, 0);
+ op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
+ }
+
+ /* If no luck, do a basic uid search */
+ if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
+ nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
+ if (!BER_BVISEMPTY(&pi->dn)) {
+ sdn = pi->dn;
+ dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
+ }
+ }
+ if (BER_BVISEMPTY(&pi->dn)) {
+ return NSLCD_PAM_USER_UNKNOWN;
+ }
+ return 0;
+}
+
+int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
+ struct paminfo *pi)
+{
+ int rc;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+
+ pi->msg.bv_val = pi->pwd.bv_val;
+ pi->msg.bv_len = 0;
+ pi->authz = NSLCD_PAM_SUCCESS;
+
+ if (!pi->ispwdmgr) {
+
+ rc = pam_uid2dn(ni, op, pi);
+ if (rc) goto finish;
+
+ if (BER_BVISEMPTY(&pi->pwd)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+
+ /* Should only need to do this once at open time, but there's always
+ * the possibility that ppolicy will get loaded later.
+ */
+ if (!ppolicy_cid) {
+ rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ &ppolicy_cid);
+ }
+ /* of course, 0 is a valid cid, but it won't be ppolicy... */
+ if (ppolicy_cid) {
+ op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
+ }
+ }
+
+ cb.sc_response = pam_bindcb;
+ cb.sc_private = pi;
+ op->o_callback = &cb;
+ op->o_dn.bv_val[0] = 0;
+ op->o_dn.bv_len = 0;
+ op->o_ndn.bv_val[0] = 0;
+ op->o_ndn.bv_len = 0;
+ op->o_tag = LDAP_REQ_BIND;
+ op->o_protocol = LDAP_VERSION3;
+ op->orb_method = LDAP_AUTH_SIMPLE;
+ op->orb_cred = pi->pwd;
+ op->o_req_dn = pi->dn;
+ op->o_req_ndn = pi->dn;
+ slap_op_time( &op->o_time, &op->o_tincr );
+ rc = op->o_bd->be_bind( op, &rs );
+ memset(pi->pwd.bv_val,0,pi->pwd.bv_len);
+ /* quirk: on successful bind, caller has to send result. we need
+ * to make sure callbacks run.
+ */
+ if (rc == LDAP_SUCCESS)
+ send_ldap_result(op, &rs);
+ switch(rs.sr_err) {
+ case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
+ case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
+ default: rc = NSLCD_PAM_AUTH_ERR; break;
+ }
+finish:
+ Debug(LDAP_DEBUG_ANY,"pam_do_bind (%s): rc (%d)\n",
+ pi->dn.bv_val ? pi->dn.bv_val : "NULL", rc );
+ return rc;
+}
+
+int pam_authc(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid)
+{
+ int32_t tmpint32;
+ int rc;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ char pwdc[256];
+ struct paminfo pi;
+
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+ READ_STRING(fp,pwdc);
+ pi.pwd.bv_val = pwdc;
+ pi.pwd.bv_len = tmpint32;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",
+ pi.uid.bv_val ? pi.uid.bv_val : "NULL" );
+
+ BER_BVZERO(&pi.msg);
+ pi.ispwdmgr = 0;
+
+ /* if service is "passwd" and "nssov-pam-password-prohibit-message */
+ /* is set, deny the auth request */
+ if (!strcmp(svcc, "passwd") &&
+ !BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(): %s (%s)\n",
+ "password_prohibit_message for passwd",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+
+ /* if username is null, pwdmgr password preliminary check */
+ if (BER_BVISEMPTY(&pi.uid)) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) {
+ /* pwdmgr dn not configured */
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "pwdmgr dn not configured" );
+ ber_str2bv("pwdmgr dn not configured", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ } else if (calleruid != 0) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "caller is not root" );
+ ber_str2bv("only root may do that", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ } else {
+ /* use pwdmgr dn */
+ ber_str2bv(ni->ni_pam_pwdmgr_dn.bv_val, 0, 0, &pi.dn);
+ }
+
+ /* use pwdmgr pwd if configured */
+ if (BER_BVISEMPTY(&pi.pwd)) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_pwd)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "no pwdmgr pwd" );
+ ber_str2bv("pwdmgr pwd not configured", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ /* use configured pwdmgr pwd */
+ memset((void *) pwdc, 0, 256);
+ strncpy(pi.pwd.bv_val, ni->ni_pam_pwdmgr_pwd.bv_val,
+ ni->ni_pam_pwdmgr_pwd.bv_len);
+ pi.pwd.bv_len = ni->ni_pam_pwdmgr_pwd.bv_len;
+ }
+ pi.ispwdmgr = 1;
+ }
+
+
+ rc = pam_do_bind(ni, fp, op, &pi);
+
+finish:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s): rc (%d)\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL",rc );
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&pi.uid);
+ WRITE_INT32(fp,pi.authz); /* authz */
+ WRITE_BERVAL(fp,&pi.msg); /* authzmsg */
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+static struct berval grpmsg =
+ BER_BVC("Access denied by group check");
+static struct berval hostmsg =
+ BER_BVC("Access denied for this host");
+static struct berval svcmsg =
+ BER_BVC("Access denied for this service");
+static struct berval uidmsg =
+ BER_BVC("Access denied by UID check");
+
+static int pam_compare_cb(Operation *op, SlapReply *rs)
+{
+ if (rs->sr_err == LDAP_COMPARE_TRUE)
+ op->o_callback->sc_private = (void *)1;
+ return LDAP_SUCCESS;
+}
+
+int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ struct berval authzmsg = BER_BVNULL;
+ int32_t tmpint32;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ int rc;
+ struct paminfo pi;
+ Entry *e = NULL;
+ Attribute *a;
+ slap_callback cb = {0};
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto finish;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL" );
+
+ /* See if they have access to the host and service */
+ if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ struct berval hostdn = BER_BVNULL;
+ struct berval odn = op->o_ndn;
+ SlapReply rs = {REP_RESULT};
+ op->o_dn = pi.dn;
+ op->o_ndn = pi.dn;
+ {
+ nssov_mapinfo *mi = &ni->ni_maps[NM_host];
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf),fbuf};
+ SlapReply rs2 = {REP_RESULT};
+
+ /* Lookup the host entry */
+ nssov_filter_byname(mi,0,&global_host_bv,&filter);
+ cb.sc_private = &hostdn;
+ cb.sc_response = nssov_name2dn_cb;
+ op->o_callback = &cb;
+ op->o_req_dn = mi->mi_base;
+ op->o_req_ndn = mi->mi_base;
+ op->ors_scope = mi->mi_scope;
+ op->ors_filterstr = filter;
+ op->ors_filter = str2filter_x(op, filter.bv_val);
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = 2;
+ rc = op->o_bd->be_search(op, &rs2);
+ filter_free_x(op, op->ors_filter, 1);
+
+ if (BER_BVISEMPTY(&hostdn) &&
+ !BER_BVISEMPTY(&ni->ni_pam_defhost)) {
+ filter.bv_len = sizeof(fbuf);
+ filter.bv_val = fbuf;
+ rs_reinit(&rs2, REP_RESULT);
+ nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
+ op->ors_filterstr = filter;
+ op->ors_filter = str2filter_x(op, filter.bv_val);
+ rc = op->o_bd->be_search(op, &rs2);
+ filter_free_x(op, op->ors_filter, 1);
+ }
+
+ /* no host entry, no default host -> deny */
+ if (BER_BVISEMPTY(&hostdn)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = hostmsg;
+ goto finish;
+ }
+ }
+
+ cb.sc_response = pam_compare_cb;
+ cb.sc_private = NULL;
+ op->o_tag = LDAP_REQ_COMPARE;
+ op->o_req_dn = hostdn;
+ op->o_req_ndn = hostdn;
+ ava.aa_desc = nssov_pam_svc_ad;
+ ava.aa_value = pi.svc;
+ op->orc_ava = &ava;
+ rc = op->o_bd->be_compare( op, &rs );
+ if ( cb.sc_private == NULL ) {
+ authzmsg = svcmsg;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ op->o_dn = odn;
+ op->o_ndn = odn;
+ }
+
+ /* See if they're a member of the group */
+ if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
+ !BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
+ ni->ni_pam_group_ad) {
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ SlapReply rs = {REP_RESULT};
+ op->o_callback = &cb;
+ cb.sc_response = pam_compare_cb;
+ cb.sc_private = NULL;
+ op->o_tag = LDAP_REQ_COMPARE;
+ op->o_req_dn = ni->ni_pam_group_dn;
+ op->o_req_ndn = ni->ni_pam_group_dn;
+ ava.aa_desc = ni->ni_pam_group_ad;
+ ava.aa_value = pi.dn;
+ op->orc_ava = &ava;
+ rc = op->o_bd->be_compare( op, &rs );
+ if ( cb.sc_private == NULL ) {
+ authzmsg = grpmsg;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ }
+
+ /* We need to check the user's entry for these bits */
+ if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
+ ni->ni_pam_template_ad ||
+ ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
+ rc = be_entry_get_rw( op, &pi.dn, NULL, NULL, 0, &e );
+ if (rc != LDAP_SUCCESS) {
+ rc = NSLCD_PAM_USER_UNKNOWN;
+ goto finish;
+ }
+ }
+ if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
+ a = attr_find(e->e_attrs, nssov_pam_host_ad);
+ if (!a || attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_VALUE_OF_SYNTAX,
+ &global_host_bv, NULL, op->o_tmpmemctx )) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = hostmsg;
+ goto finish;
+ }
+ }
+ if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
+ a = attr_find(e->e_attrs, nssov_pam_svc_ad);
+ if (!a || attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_VALUE_OF_SYNTAX,
+ &pi.svc, NULL, op->o_tmpmemctx )) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = svcmsg;
+ goto finish;
+ }
+ }
+
+/* from passwd.c */
+#define UIDN_KEY 2
+
+ if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
+ int id;
+ char *tmp;
+ nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
+ a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
+ if (!a) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
+ (ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ }
+
+ if (ni->ni_pam_template_ad) {
+ a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
+ if (a)
+ pi.uid = a->a_vals[0];
+ else if (!BER_BVISEMPTY(&ni->ni_pam_template))
+ pi.uid = ni->ni_pam_template;
+ }
+ rc = NSLCD_PAM_SUCCESS;
+
+finish:
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&authzmsg);
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ if (e) {
+ be_entry_release_r(op, e);
+ }
+ switch (rc) {
+ case NSLCD_PAM_SUCCESS:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): success\n" );
+ break;
+ case NSLCD_PAM_PERM_DENIED:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): %s\n",
+ authzmsg.bv_val ? authzmsg.bv_val : "NULL" );
+ break;
+ default:
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_authz(): permission denied, rc (%d)\n",
+ rc );
+ }
+ return 0;
+}
+
+static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
+{
+ int32_t tmpint32;
+ char svcc[256];
+ char uidc[32];
+ char ttyc[32];
+ char rhostc[256];
+ char ruserc[32];
+ char sessionID[64];
+ struct paminfo pi;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
+ struct berval timestamp, bv[2], *nbv;
+ time_t stamp;
+ Modifications mod;
+ int rc = 0;
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+
+ if (action==NSLCD_ACTION_PAM_SESS_O) {
+ slap_op_time( &op->o_time, &op->o_tincr );
+ timestamp.bv_len = sizeof(timebuf);
+ timestamp.bv_val = timebuf;
+ stamp = op->o_time;
+ slap_timestamp( &stamp, &timestamp );
+ } else {
+ READ_STRING(fp,sessionID);
+ timestamp.bv_val = sessionID;
+ timestamp.bv_len = tmpint32;
+ }
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto done;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', pi.dn.bv_val );
+
+ if (!ni->ni_pam_sessions) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(): %s\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c',
+ "pam session(s) not configured, ignored" );
+ rc = -1;
+ goto done;
+ }
+
+ {
+ int i, found=0;
+ for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
+ if (ni->ni_pam_sessions[i].bv_len != pi.svc.bv_len)
+ continue;
+ if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, pi.svc.bv_val)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): service(%s) not configured, ignored\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c',
+ pi.svc.bv_val );
+ rc = -1;
+ goto done;
+ }
+ }
+
+ bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + pi.svc.bv_len +
+ pi.tty.bv_len + pi.ruser.bv_len + pi.rhost.bv_len + STRLENOF(" (@)");
+ bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
+ sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
+ timestamp.bv_val, global_host_bv.bv_val, pi.svc.bv_val, pi.tty.bv_val,
+ pi.ruser.bv_val, pi.rhost.bv_val);
+
+ Debug(LDAP_DEBUG_TRACE, "nssov_pam_sess_%c(): loginStatus (%s) \n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', bv[0].bv_val );
+
+ mod.sml_numvals = 1;
+ mod.sml_values = bv;
+ BER_BVZERO(&bv[1]);
+ attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
+ mod.sml_nvalues = nbv;
+ mod.sml_desc = ad_loginStatus;
+ mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
+ LDAP_MOD_DELETE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ op->o_callback = &cb;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = pi.dn;
+ op->o_req_ndn = pi.dn;
+ if (op->o_bd->be_modify( op, &rs ) != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): modify op failed\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c' );
+ rc = -1;
+ }
+
+ if ( mod.sml_next ) {
+ slap_mods_free( mod.sml_next, 1 );
+ }
+ ber_bvarray_free_x( nbv, op->o_tmpmemctx );
+
+done:;
+
+ if (rc == 0) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): success\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c' );
+ }
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,action);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ if (action==NSLCD_ACTION_PAM_SESS_O)
+ WRITE_STRING(fp,timestamp.bv_val);
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
+}
+
+int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
+}
+
+int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid)
+{
+ struct berval npw;
+ int32_t tmpint32;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ int asroot;
+ char opwc[256];
+ char npwc[256];
+ struct paminfo pi;
+ int rc;
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = svcc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = svcc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = svcc;
+ pi.tty.bv_len = tmpint32;
+ READ_INT32(fp, asroot);
+ READ_STRING(fp,opwc);
+ pi.pwd.bv_val = opwc;
+ pi.pwd.bv_len = tmpint32;
+ READ_STRING(fp,npwc);
+ npw.bv_val = npwc;
+ npw.bv_len = tmpint32;
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto done;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s %s\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL",
+ pi.uid.bv_val ? pi.uid.bv_val : "NULL",
+ asroot ? "as root" : "as user");
+
+ BER_BVZERO(&pi.msg);
+ pi.ispwdmgr = 0;
+
+ /* nssov_pam prohibits password mod */
+ if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s (%s)\n",
+ "password_prohibit_message",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+
+ if (asroot) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n",
+ "pwdmgr not configured" );
+ ber_str2bv("pwdmgr not configured", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+ if (calleruid != 0) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s\n",
+ "caller is not root" );
+ ber_str2bv("only root may do that", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+ /* root user requesting pwmod */
+ pi.ispwdmgr = 1;
+ }
+
+ if (!pi.ispwdmgr && BER_BVISEMPTY(&pi.pwd)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n",
+ "not pwdmgr and old pwd empty" );
+ ber_str2bv("must provide old password", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval bv;
+ SlapReply rs = {REP_RESULT};
+ slap_callback cb = {0};
+
+ ber_init_w_nullc(ber, LBER_USE_DER);
+ ber_printf(ber, "{");
+ if (!BER_BVISEMPTY(&pi.dn))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
+ &pi.dn);
+ /* supply old pwd whenever it's given */
+ if (!BER_BVISEMPTY(&pi.pwd))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
+ &pi.pwd);
+ if (!BER_BVISEMPTY(&npw))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
+ &npw);
+ ber_printf(ber, "N}");
+ ber_flatten2(ber, &bv, 0);
+ op->o_tag = LDAP_REQ_EXTENDED;
+ op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
+ op->ore_reqdata = &bv;
+
+ if (pi.ispwdmgr) {
+ /* root user changing end-user passwords */
+ op->o_dn = ni->ni_pam_pwdmgr_dn;
+ op->o_ndn = ni->ni_pam_pwdmgr_dn;
+ } else {
+ /* end-user self-pwd-mod */
+ op->o_dn = pi.dn;
+ op->o_ndn = pi.dn;
+ }
+ op->o_callback = &cb;
+ op->o_conn->c_authz_backend = op->o_bd;
+ cb.sc_response = slap_null_cb;
+ op->o_bd = frontendDB;
+ rc = op->o_bd->be_extended(op, &rs);
+ if (rs.sr_text)
+ ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
+ if (rc == LDAP_SUCCESS)
+ rc = NSLCD_PAM_SUCCESS;
+ else
+ rc = NSLCD_PAM_PERM_DENIED;
+
+done:;
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), rc (%d)\n", rc );
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&pi.msg);
+ return 0;
+}
+
+int nssov_pam_init()
+{
+ int code = 0;
+ const char *text;
+ if (!ad_loginStatus)
+ code = slap_str2ad("loginStatus", &ad_loginStatus, &text);
+
+ return code;
+}