diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
commit | 5ea77a75dd2d2158401331879f3c8f47940a732c (patch) | |
tree | d89dc06e9f4850a900f161e25f84e922c4f86cc8 /contrib/slapd-modules/nssov/pam.c | |
parent | Initial commit. (diff) | |
download | openldap-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.c | 862 |
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, ×tamp ); + } 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; +} |