#include <security/pam_modules.h>
-static int pam_nullcb(
+static int ppolicy_cid;
+
+struct bindinfo {
+ int authz;
+ struct berval msg;
+};
+
+static int pam_bindcb(
Operation *op, SlapReply *rs)
{
+ struct bindinfo *bi = 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
+ bi->msg.bv_len = sprintf(bi->msg.bv_val,
+ "\nWARNING: Password expires in %d %s\n", expire, unit);
+ } else if (grace > 0) {
+ bi->msg.bv_len = sprintf(bi->msg.bv_val,
+ "Password expired; %d grace logins remaining",
+ grace);
+ bi->authz = PAM_NEW_AUTHTOK_REQD;
+ } else if (error != PP_noError) {
+ ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
+ &bi->msg);
+ switch (error) {
+ case PP_passwordExpired:
+ /* report this during authz */
+ rs->sr_err = LDAP_SUCCESS;
+ /* fallthru */
+ case PP_changeAfterReset:
+ bi->authz = PAM_NEW_AUTHTOK_REQD;
+ }
+ }
+ }
+ ldap_ld_free(ld,0,NULL,NULL);
+ }
+ }
return LDAP_SUCCESS;
}
char pwdc[256];
struct berval uid, svc, pwd, sdn, dn;
int hlen;
+ struct bindinfo bi;
+
+ bi.authz = PAM_SUCCESS;
+ bi.msg.bv_val = pwdc;
+ bi.msg.bv_len = 0;
READ_STRING_BUF2(fp,uidc,sizeof(uidc));
uid.bv_val = uidc;
}
BER_BVZERO(&sdn);
- /* TODO: add ppolicy control */
- cb.sc_response = pam_nullcb;
+ /* 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 = &bi;
op->o_callback = &cb;
op->o_dn.bv_val[0] = 0;
op->o_dn.bv_len = 0;
op->o_req_dn = dn;
op->o_req_ndn = dn;
slap_op_time( &op->o_time, &op->o_tincr );
- op->o_bd->be_bind( op, &rs );
+ rc = op->o_bd->be_bind( op, &rs );
memset(pwd.bv_val,0,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 = PAM_SUCCESS; break;
case LDAP_INVALID_CREDENTIALS: rc = PAM_AUTH_ERR; break;
WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
WRITE_INT32(fp,rc);
- WRITE_INT32(fp,PAM_SUCCESS); /* authz */
+ WRITE_INT32(fp,bi.authz); /* authz */
WRITE_BERVAL(fp,&dn);
- WRITE_BERVAL(fp,&sdn); /* authzmsg */
- WRITE_BERVAL(fp,&sdn); /* tmpluser */
+ WRITE_BERVAL(fp,&bi.msg); /* authzmsg */
return 0;
}
{
struct berval dn, svc;
struct berval authzmsg = BER_BVNULL;
+ struct berval tmpluser = BER_BVNULL;
int32_t tmpint32;
char dnc[1024];
char svcc[256];
WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
WRITE_INT32(fp,PAM_SUCCESS);
WRITE_BERVAL(fp,&authzmsg);
+ WRITE_BERVAL(fp,&tmpluser);
return 0;
}