diff --git a/SOURCES/0016-Backport-of-request-and-parse-password-policy-contro.patch b/SOURCES/0016-Backport-of-request-and-parse-password-policy-contro.patch new file mode 100644 index 0000000..8bc5ef8 --- /dev/null +++ b/SOURCES/0016-Backport-of-request-and-parse-password-policy-contro.patch @@ -0,0 +1,335 @@ +From 329532b3ad43642d50815d089fa3e75746a694d8 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 13 Aug 2019 21:51:44 +0200 +Subject: [PATCH 16/23] Backport of request and parse password policy controls + when doing user authentication in nslcd + +--- + compat/ldap_compat.h | 14 +++ + configure.ac | 2 + + nslcd/myldap.c | 217 +++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 224 insertions(+), 9 deletions(-) + +diff --git a/compat/ldap_compat.h b/compat/ldap_compat.h +index 039932c..bca8e35 100644 +--- a/compat/ldap_compat.h ++++ b/compat/ldap_compat.h +@@ -70,4 +70,18 @@ int ldap_passwd_s(LDAP *ld,struct berval *user,struct berval *oldpw, + #endif /* LDAP_OPT_ERROR_STRING */ + #endif /* not LDAP_OPT_DIAGNOSTIC_MESSAGE */ + ++/* provide replacement oid definitions */ ++#ifndef LDAP_CONTROL_PWEXPIRED ++#define LDAP_CONTROL_PWEXPIRED "2.16.840.1.113730.3.4.4" ++#endif /* LDAP_CONTROL_PWEXPIRED */ ++#ifndef LDAP_CONTROL_PWEXPIRING ++#define LDAP_CONTROL_PWEXPIRING "2.16.840.1.113730.3.4.5" ++#endif /* LDAP_CONTROL_PWEXPIRING */ ++#ifndef LDAP_CONTROL_PASSWORDPOLICYREQUEST ++#define LDAP_CONTROL_PASSWORDPOLICYREQUEST "1.3.6.1.4.1.42.2.27.8.5.1" ++#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */ ++#ifndef LDAP_CONTROL_PASSWORDPOLICYRESPONSE ++#define LDAP_CONTROL_PASSWORDPOLICYRESPONSE "1.3.6.1.4.1.42.2.27.8.5.1" ++#endif /* LDAP_CONTROL_PASSWORDPOLICYRESPONSE */ ++ + #endif /* COMPAT__LDAP_COMPAT_H */ +diff --git a/configure.ac b/configure.ac +index bdb2792..344fa55 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -697,6 +697,8 @@ then + AC_CHECK_FUNCS(ldap_parse_result ldap_memfree ldap_controls_free ldap_control_free) + AC_CHECK_FUNCS(ldap_explode_dn ldap_explode_rdn ldap_set_option ldap_get_option) + AC_CHECK_FUNCS(ldap_abandon ldap_simple_bind_s ldap_unbind ldap_set_rebind_proc) ++ AC_CHECK_FUNCS(ldap_sasl_bind ldap_sasl_bind_s ldap_control_find) ++ AC_CHECK_FUNCS(ldap_parse_passwordpolicy_control ldap_passwordpolicy_err2txt) + AC_CHECK_FUNCS(ldap_initialize ldap_search_ext ldap_start_tls_s) + AC_CHECK_FUNCS(ldap_create_control ldap_extended_operation_s) + AC_CHECK_FUNCS(ldap_domain2hostlist ldap_domain2dn) +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index 895b682..64b7f13 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -85,16 +85,20 @@ struct ldap_session + { + /* the connection */ + LDAP *ld; +- /* the username to bind with */ +- char binddn[256]; +- /* the password to bind with if any */ +- char bindpw[128]; + /* timestamp of last activity */ + time_t lastactivity; + /* index into ldc_uris: currently connected LDAP uri */ + int current_uri; + /* a list of searches registered with this session */ + struct myldap_search *searches[MAX_SEARCHES_IN_SESSION]; ++ /* the username to bind with */ ++ char binddn[256]; ++ /* the password to bind with if any */ ++ char bindpw[128]; ++ /* the authentication result (NSLCD_PAM_* code) */ ++ int policy_response; ++ /* the authentication message */ ++ char policy_message[1024]; + }; + + /* A search description set as returned by myldap_search(). */ +@@ -307,12 +311,14 @@ static MYLDAP_SESSION *myldap_session_new(void) + } + /* initialize the session */ + session->ld=NULL; +- session->binddn[0]='\0'; +- session->bindpw[0]='\0'; + session->lastactivity=0; + session->current_uri=0; + for (i=0;isearches[i]=NULL; ++ session->binddn[0]='\0'; ++ session->bindpw[0]='\0'; ++ session->policy_response = NSLCD_PAM_SUCCESS; ++ session->policy_message[0] = '\0'; + /* return the new session */ + return session; + } +@@ -398,6 +404,195 @@ static int do_sasl_interact(LDAP UNUSED(*ld),unsigned UNUSED(flags),void *defaul + } + #endif /* HAVE_SASL_INTERACT_T */ + ++#if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE) ++static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls) ++{ ++ int i; ++ int rc; ++ /* clear policy response information in session */ ++ session->policy_response = NSLCD_PAM_SUCCESS; ++ strncpy(session->policy_message, "", sizeof(session->policy_message)); ++ for (i = 0; ctrls[i] != NULL; i++) ++ { ++ if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRED) == 0) ++ { ++ /* check for expired control: force the user to change their password */ ++ log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRED (password expired, user should change)"); ++ if (session->policy_response == NSLCD_PAM_SUCCESS) ++ session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; ++ } ++ else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRING) == 0) ++ { ++ /* check for password expiration warning control: the password is about ++ to expire (returns the number of seconds remaining until the password ++ expires) */ ++ char seconds[32]; ++ long int sec; ++ mysnprintf(seconds, sizeof(seconds), "%.*s", (int)ctrls[i]->ldctl_value.bv_len, ++ ctrls[i]->ldctl_value.bv_val); ++ sec = atol(seconds); ++ log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRING (password will expire in %ld seconds)", ++ sec); ++ /* return this warning to PAM */ ++ if (session->policy_response == NSLCD_PAM_SUCCESS) ++ { ++ session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "password will expire in %ld seconds", sec); ++ } ++ } ++ else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) ++ { ++ /* check for password policy control */ ++ int expire = 0, grace = 0; ++ LDAPPasswordPolicyError error = -1; ++ rc = ldap_parse_passwordpolicy_control(ld, ctrls[i], &expire, &grace, &error); ++ if (rc != LDAP_SUCCESS) ++ myldap_err(LOG_WARNING, ld, rc, "ldap_parse_passwordpolicy_control() failed (ignored)"); ++ else ++ { ++ /* log returned control information */ ++ if (error != PP_noError) ++ log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%s)", ++ ldap_passwordpolicy_err2txt(error)); ++ if (expire >= 0) ++ log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (password will expire in %d seconds)", ++ expire); ++ if (grace >= 0) ++ log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%d grace logins left)", ++ grace); ++ /* return this information to PAM */ ++ if ((error == PP_passwordExpired) && ++ ((session->policy_response == NSLCD_PAM_SUCCESS) || ++ (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) ++ { ++ session->policy_response = NSLCD_PAM_AUTHTOK_EXPIRED; ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "%s", ldap_passwordpolicy_err2txt(error)); ++ } ++ else if ((error == PP_accountLocked) && ++ ((session->policy_response == NSLCD_PAM_SUCCESS) || ++ (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) ++ { ++ session->policy_response = NSLCD_PAM_ACCT_EXPIRED; ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "%s", ldap_passwordpolicy_err2txt(error)); ++ } ++ else if ((error == PP_changeAfterReset) && ++ (session->policy_response == NSLCD_PAM_SUCCESS)) ++ { ++ session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "%s", ldap_passwordpolicy_err2txt(error)); ++ } ++ else if ((error != PP_noError) && ++ ((session->policy_response == NSLCD_PAM_SUCCESS) || ++ (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) ++ { ++ session->policy_response = NSLCD_PAM_PERM_DENIED; ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "%s", ldap_passwordpolicy_err2txt(error)); ++ } ++ else if ((expire >= 0) && ++ ((session->policy_response == NSLCD_PAM_SUCCESS) || ++ (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) ++ { ++ session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expire in %d seconds", expire); ++ } ++ else if ((grace >= 0) && ++ (session->policy_response == NSLCD_PAM_SUCCESS)) ++ { ++ session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password expired, %d grace logins left", grace); ++ } ++ } ++ } ++ /* ignore any other controls */ ++ } ++} ++static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri) ++{ ++ int rc, parserc; ++ struct berval cred; ++ LDAPControl passwd_policy_req; ++ LDAPControl *requestctrls[2]; ++ LDAPControl **responsectrls; ++ int msgid; ++ struct timeval timeout; ++ LDAPMessage *result; ++ /* build password policy request control */ ++ passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST; ++ passwd_policy_req.ldctl_value.bv_val = NULL; /* none */ ++ passwd_policy_req.ldctl_value.bv_len = 0; ++ passwd_policy_req.ldctl_iscritical = 0; /* not critical */ ++ requestctrls[0] = &passwd_policy_req; ++ requestctrls[1] = NULL; ++ /* build password berval */ ++ cred.bv_val = (char *)session->bindpw; ++ cred.bv_len = (session->bindpw == NULL) ? 0 : strlen(session->bindpw); ++ /* do a SASL simple bind with the binddn and bindpw */ ++ log_log(LOG_DEBUG, "ldap_sasl_bind(\"%s\",%s) (uri=\"%s\")", session->binddn, ++ ((session->bindpw != NULL) && (session->bindpw[0] != '\0')) ? "\"***\"" : "\"\"", uri); ++ rc = ldap_sasl_bind(ld, session->binddn, LDAP_SASL_SIMPLE, &cred, requestctrls, NULL, &msgid); ++ if (rc != LDAP_SUCCESS) ++ return rc; ++ if (msgid == -1) ++ { ++ myldap_err(LOG_WARNING, ld, rc,"ldap_sasl_bind() failed (msgid=-1, uri=%s)", uri); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ /* get the result from the bind operation */ ++ timeout.tv_sec = nslcd_cfg->ldc_bind_timelimit; ++ timeout.tv_usec = 0; ++ result = NULL; ++ rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &timeout, &result); ++ if (rc == -1) /* some error */ ++ { ++ if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS) ++ rc = LDAP_UNAVAILABLE; ++ myldap_err(LOG_ERR, ld, rc, "ldap_result() failed"); ++ if (result != NULL) ++ ldap_msgfree(result); ++ return LDAP_LOCAL_ERROR; ++ } ++ if (rc == 0) /* the timeout expired */ ++ { ++ log_log(LOG_ERR, "ldap_result() timed out"); ++ if (result != NULL) ++ ldap_msgfree(result); ++ return LDAP_TIMEOUT; ++ } ++ /* parse the result from the bind operation (frees result, get controls) */ ++ responsectrls = NULL; ++ parserc = ldap_parse_result(ld, result, &rc, NULL, NULL, NULL, &responsectrls, 1); ++ if (parserc != LDAP_SUCCESS) ++ { ++ myldap_err(LOG_ERR, ld, parserc, "ldap_parse_result() failed"); ++ if (responsectrls != NULL) ++ ldap_controls_free(responsectrls); ++ return parserc; ++ } ++ if (rc != LDAP_SUCCESS) ++ { ++ myldap_err(LOG_ERR, ld, rc, "ldap_parse_result() failed"); ++ if (responsectrls != NULL) ++ ldap_controls_free(responsectrls); ++ return rc; ++ } ++ /* check the returned controls */ ++ if (responsectrls != NULL) ++ { ++ handle_ppasswd_controls(session, ld, responsectrls); ++ /* free controls */ ++ ldap_controls_free(responsectrls); ++ } ++ return LDAP_SUCCESS; ++} ++#endif /* no SASL, so no ppolicy */ ++ + #define LDAP_SET_OPTION(ld,option,invalue) \ + rc=ldap_set_option(ld,option,invalue); \ + if (rc!=LDAP_SUCCESS) \ +@@ -410,7 +605,7 @@ static int do_sasl_interact(LDAP UNUSED(*ld),unsigned UNUSED(flags),void *defaul + The binddn and bindpw parameters may be used to override the authentication + mechanism defined in the configuration. This returns an LDAP result + code. */ +-static int do_bind(LDAP *ld,const char *binddn,const char *bindpw,const char *uri) ++static int do_bind(MYLDAP_SESSION *session, LDAP *ld,const char *binddn,const char *bindpw,const char *uri) + { + int rc; + #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S +@@ -435,10 +630,14 @@ static int do_bind(LDAP *ld,const char *binddn,const char *bindpw,const char *ur + /* check if the binddn and bindpw are overwritten in the session */ + if ((binddn!=NULL)&&(binddn[0]!='\0')) + { ++#if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE) ++ return do_ppolicy_bind(session, ld, uri); ++#else /* no SASL, so no ppolicy */ + /* do a simple bind */ + log_log(LOG_DEBUG,"ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",binddn, + ((bindpw!=NULL)&&(bindpw[0]!='\0'))?"\"***\"":"\"\"",uri); + return ldap_simple_bind_s(ld,binddn,bindpw); ++#endif + } + /* perform SASL bind if requested and available on platform */ + #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S +@@ -504,7 +703,7 @@ static int do_rebind(LDAP *ld,LDAP_CONST char *url, + { + MYLDAP_SESSION *session=(MYLDAP_SESSION *)arg; + log_log(LOG_DEBUG,"rebinding to %s",url); +- return do_bind(ld,session->binddn,session->bindpw,url); ++ return do_bind(session,ld,session->binddn,session->bindpw,url); + } + #else /* not recent OpenLDAP */ + static int do_rebind(LDAP *ld,char **dnp,char **passwdp,int *authmethodp, +@@ -798,7 +997,7 @@ static int do_open(MYLDAP_SESSION *session) + } + /* bind to the server */ + errno=0; +- rc=do_bind(session->ld,session->binddn,session->bindpw, ++ rc=do_bind(session, session->ld,session->binddn,session->bindpw, + nslcd_cfg->ldc_uris[session->current_uri].uri); + if (rc!=LDAP_SUCCESS) + { +-- +2.20.1 + diff --git a/SOURCES/0017-Backport-of-passing-expiration-controls-back-to-PAM-.patch b/SOURCES/0017-Backport-of-passing-expiration-controls-back-to-PAM-.patch new file mode 100644 index 0000000..bc5de38 --- /dev/null +++ b/SOURCES/0017-Backport-of-passing-expiration-controls-back-to-PAM-.patch @@ -0,0 +1,106 @@ +From 289cd5ab7d125c8eb4a5e85800ab8f5f54dc4519 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 13 Aug 2019 22:06:12 +0200 +Subject: [PATCH 17/23] Backport of passing expiration controls back to PAM + client + +--- + nslcd/myldap.c | 11 +++++++++++ + nslcd/myldap.h | 5 +++++ + nslcd/pam.c | 15 ++++++++++++--- + 3 files changed, 28 insertions(+), 3 deletions(-) + +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index 64b7f13..9f6b4b0 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -1024,6 +1024,17 @@ void myldap_set_credentials(MYLDAP_SESSION *session,const char *dn, + session->bindpw[sizeof(session->bindpw)-1]='\0'; + } + ++/* Get bind ppolicy results from the last bind operation. This function ++ returns a NSLCD_PAM_* code and optional message. */ ++void myldap_get_policy_response(MYLDAP_SESSION *session, int *response, ++ const char **message) ++{ ++ if (response != NULL) ++ *response = session->policy_response; ++ if (message != NULL) ++ *message = session->policy_message; ++} ++ + static int do_try_search(MYLDAP_SEARCH *search) + { + int rc; +diff --git a/nslcd/myldap.h b/nslcd/myldap.h +index f118f72..3a99765 100644 +--- a/nslcd/myldap.h ++++ b/nslcd/myldap.h +@@ -72,6 +72,11 @@ MUST_USE MYLDAP_SESSION *myldap_create_session(void); + void myldap_set_credentials(MYLDAP_SESSION *session,const char *dn, + const char *password); + ++/* Get bind ppolicy results from the last bind operation. This function ++ returns a NSLCD_PAM_* code and optional message. */ ++void myldap_get_policy_response(MYLDAP_SESSION *session, int *response, ++ const char **message); ++ + /* Closes all pending searches and deallocates any memory that is allocated + with these searches. This does not close the session. */ + void myldap_session_cleanup(MYLDAP_SESSION *session); +diff --git a/nslcd/pam.c b/nslcd/pam.c +index ee28725..40a8687 100644 +--- a/nslcd/pam.c ++++ b/nslcd/pam.c +@@ -41,13 +41,15 @@ + + /* set up a connection and try to bind with the specified DN and password, + returns an LDAP result code */ +-static int try_bind(const char *userdn,const char *password) ++static int try_bind(const char *userdn,const char *password, ++ int *authzrc, char *authzmsg, size_t authzmsgsz) + { + MYLDAP_SESSION *session; + MYLDAP_SEARCH *search; + MYLDAP_ENTRY *entry; + static const char *attrs[2]; + int rc; ++ const char *msg; + /* set up a new connection */ + session=myldap_create_session(); + if (session==NULL) +@@ -74,6 +76,13 @@ static int try_bind(const char *userdn,const char *password) + log_log(LOG_WARNING,"%s: lookup failed: %s",userdn,ldap_err2string(rc)); + } + } ++ /* get any policy response from the bind */ ++ myldap_get_policy_response(session, authzrc, &msg); ++ if ((msg != NULL) && (msg[0] != '\0')) ++ { ++ mysnprintf(authzmsg, authzmsgsz - 1, "%s", msg); ++ log_log(LOG_WARNING, "%s: %s", userdn, authzmsg); ++ } + /* close the session */ + myldap_session_close(session); + /* return results */ +@@ -297,7 +306,7 @@ int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid) + update_username(entry,username,sizeof(username)); + } + /* try authentication */ +- rc=try_bind(userdn,password); ++ rc = try_bind(userdn, password, &authzrc, authzmsg, sizeof(authzmsg)); + if (rc==LDAP_SUCCESS) + log_log(LOG_DEBUG,"bind successful"); + /* map result code */ +@@ -308,7 +317,7 @@ int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid) + default: rc=NSLCD_PAM_AUTH_ERR; + } + /* perform shadow attribute checks */ +- if (*username!='\0') ++ if ((*username != '\0') && (authzrc == NSLCD_PAM_SUCCESS)) + authzrc=check_shadow(session,username,authzmsg,sizeof(authzmsg),1,0); + /* write response */ + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); +-- +2.20.1 + diff --git a/SOURCES/0018-Also-extract-policy-controls-on-BIND-failure.patch b/SOURCES/0018-Also-extract-policy-controls-on-BIND-failure.patch new file mode 100644 index 0000000..629e3c5 --- /dev/null +++ b/SOURCES/0018-Also-extract-policy-controls-on-BIND-failure.patch @@ -0,0 +1,62 @@ +From e5a7c13c7aa70ebe59764761020fc509dd0ec33a Mon Sep 17 00:00:00 2001 +From: Arthur de Jong +Date: Sun, 4 May 2014 23:16:03 +0200 +Subject: [PATCH 18/23] Also extract policy controls on BIND failure + +This ensures that controls returned by an LDAP server as part of a +failed BIND operation are also returned. This makes it possible to +distinguish between a wrong password and an expired password. + +This also only logs the BIND operation result on DEBUG level (the error +is logged later on). + +(cherry picked from commit ca36a50143eb38c9040c2567172b9bfb9dba1838) +--- + nslcd/myldap.c | 19 +++++++++---------- + 1 file changed, 9 insertions(+), 10 deletions(-) + +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index 9f6b4b0..e33296f 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -565,7 +565,7 @@ static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri) + ldap_msgfree(result); + return LDAP_TIMEOUT; + } +- /* parse the result from the bind operation (frees result, get controls) */ ++ /* parse the result from the bind operation (frees result, gets controls) */ + responsectrls = NULL; + parserc = ldap_parse_result(ld, result, &rc, NULL, NULL, NULL, &responsectrls, 1); + if (parserc != LDAP_SUCCESS) +@@ -575,20 +575,19 @@ static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri) + ldap_controls_free(responsectrls); + return parserc; + } +- if (rc != LDAP_SUCCESS) +- { +- myldap_err(LOG_ERR, ld, rc, "ldap_parse_result() failed"); +- if (responsectrls != NULL) +- ldap_controls_free(responsectrls); +- return rc; +- } +- /* check the returned controls */ ++ /* handle any returned controls */ + if (responsectrls != NULL) + { + handle_ppasswd_controls(session, ld, responsectrls); +- /* free controls */ + ldap_controls_free(responsectrls); + } ++ /* return the result of the BIND operation */ ++ if (rc != LDAP_SUCCESS) ++ { ++ myldap_err(LOG_DEBUG, ld, rc, "ldap_parse_result() result"); ++ return rc; ++ } ++ /* check the returned controls */ + return LDAP_SUCCESS; + } + #endif /* no SASL, so no ppolicy */ +-- +2.20.1 + diff --git a/SOURCES/0019-Fix-password-policy-expiration-warnings.patch b/SOURCES/0019-Fix-password-policy-expiration-warnings.patch new file mode 100644 index 0000000..a0690f2 --- /dev/null +++ b/SOURCES/0019-Fix-password-policy-expiration-warnings.patch @@ -0,0 +1,49 @@ +From e45d35b6da24c10137330fccecf681ddf32a628e Mon Sep 17 00:00:00 2001 +From: Mathieu Baeumler +Date: Thu, 9 Jul 2015 08:59:19 +0200 +Subject: [PATCH 19/23] Fix password policy expiration warnings + +If a password expiration warning (pwdExpireWarning) is set in slapd, and +the password is about to expire, slapd sends the timeBeforeExpiration +value as part of the passwordPolicyResponse. + +nslcd would incorrectly instruct the PAM module to require immediate +password change. This has been fixed for both timeBeforeExpiration and +graceLoginsRemaining. + +(cherry picked from commit 4302901a2708d55b24880b77437e3d782b0de1cb) +--- + nslcd/myldap.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index e33296f..9a24a27 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -466,7 +466,7 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + ((session->policy_response == NSLCD_PAM_SUCCESS) || + (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) + { +- session->policy_response = NSLCD_PAM_AUTHTOK_EXPIRED; ++ session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "%s", ldap_passwordpolicy_err2txt(error)); + } +@@ -497,14 +497,12 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + ((session->policy_response == NSLCD_PAM_SUCCESS) || + (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) + { +- session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "Password will expire in %d seconds", expire); + } + else if ((grace >= 0) && + (session->policy_response == NSLCD_PAM_SUCCESS)) + { +- session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "Password expired, %d grace logins left", grace); + } +-- +2.20.1 + diff --git a/SOURCES/0020-Simplify-password-policy-message-handling.patch b/SOURCES/0020-Simplify-password-policy-message-handling.patch new file mode 100644 index 0000000..523cae3 --- /dev/null +++ b/SOURCES/0020-Simplify-password-policy-message-handling.patch @@ -0,0 +1,124 @@ +From 10f9b55084a1b7d251752d5d9fe85f9d01fe93d7 Mon Sep 17 00:00:00 2001 +From: Arthur de Jong +Date: Fri, 14 Aug 2015 23:09:50 +0200 +Subject: [PATCH 20/23] Simplify password policy message handling + +This simplifies the check for overwriging pending password expiry and +grace logins warnigns and updates handling of the +LDAP_CONTROL_PWEXPIRING control to be consistent with that of the expire +value of LDAP_CONTROL_PASSWORDPOLICYRESPONSE. + +This also corrects the function name, also logs empty password policy +responses in debug mode and documents the meaning of the various +password policy values. + +(cherry picked from commit 263a44340badb1e553c997f2dfb4986fb2f4c28b) +--- + nslcd/myldap.c | 32 +++++++++++++++++++------------- + 1 file changed, 19 insertions(+), 13 deletions(-) + +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index 9a24a27..86a339e 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -405,7 +405,7 @@ static int do_sasl_interact(LDAP UNUSED(*ld),unsigned UNUSED(flags),void *defaul + #endif /* HAVE_SASL_INTERACT_T */ + + #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE) +-static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls) ++static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls) + { + int i; + int rc; +@@ -433,10 +433,9 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + sec = atol(seconds); + log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRING (password will expire in %ld seconds)", + sec); +- /* return this warning to PAM */ +- if (session->policy_response == NSLCD_PAM_SUCCESS) ++ /* return this warning so PAM can present it to the user */ ++ if (strlen(session->policy_message) == 0) + { +- session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "password will expire in %ld seconds", sec); + } +@@ -452,9 +451,8 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + else + { + /* log returned control information */ +- if (error != PP_noError) +- log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%s)", +- ldap_passwordpolicy_err2txt(error)); ++ log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%s)", ++ ldap_passwordpolicy_err2txt(error)); + if (expire >= 0) + log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (password will expire in %d seconds)", + expire); +@@ -466,6 +464,7 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + ((session->policy_response == NSLCD_PAM_SUCCESS) || + (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) + { ++ /* this means that the password has expired and must be reset */ + session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "%s", ldap_passwordpolicy_err2txt(error)); +@@ -474,6 +473,8 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + ((session->policy_response == NSLCD_PAM_SUCCESS) || + (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) + { ++ /* this means that the account is locked and the user cannot log ++ in (the bind probably failed already) */ + session->policy_response = NSLCD_PAM_ACCT_EXPIRED; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "%s", ldap_passwordpolicy_err2txt(error)); +@@ -481,6 +482,8 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + else if ((error == PP_changeAfterReset) && + (session->policy_response == NSLCD_PAM_SUCCESS)) + { ++ /* this indicates that the password must be changed before the ++ user is allowed to perform any other operation */ + session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "%s", ldap_passwordpolicy_err2txt(error)); +@@ -489,20 +492,23 @@ static void handle_ppasswd_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + ((session->policy_response == NSLCD_PAM_SUCCESS) || + (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) + { ++ /* any other error is assumed to mean that the operation failed */ + session->policy_response = NSLCD_PAM_PERM_DENIED; + mysnprintf(session->policy_message, sizeof(session->policy_message), + "%s", ldap_passwordpolicy_err2txt(error)); + } +- else if ((expire >= 0) && +- ((session->policy_response == NSLCD_PAM_SUCCESS) || +- (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD))) ++ /* both expire and grace should just be warnings to the user */ ++ if ((expire >= 0) && (strlen(session->policy_message) == 0)) + { ++ /* if no other error has happened, this indicates that the password ++ will soon expire (number of seconds) */ + mysnprintf(session->policy_message, sizeof(session->policy_message), + "Password will expire in %d seconds", expire); + } +- else if ((grace >= 0) && +- (session->policy_response == NSLCD_PAM_SUCCESS)) ++ else if ((grace >= 0) && (strlen(session->policy_message) == 0)) + { ++ /* this indicates the number of grace logins that are left before ++ no further login attempts will be allowed */ + mysnprintf(session->policy_message, sizeof(session->policy_message), + "Password expired, %d grace logins left", grace); + } +@@ -576,7 +582,7 @@ static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri) + /* handle any returned controls */ + if (responsectrls != NULL) + { +- handle_ppasswd_controls(session, ld, responsectrls); ++ handle_ppolicy_controls(session, ld, responsectrls); + ldap_controls_free(responsectrls); + } + /* return the result of the BIND operation */ +-- +2.20.1 + diff --git a/SOURCES/0021-backport-the-pam_authc_ppolicy-option.patch b/SOURCES/0021-backport-the-pam_authc_ppolicy-option.patch new file mode 100644 index 0000000..25638cc --- /dev/null +++ b/SOURCES/0021-backport-the-pam_authc_ppolicy-option.patch @@ -0,0 +1,108 @@ +From 4861574af285c3ad0188424a567648673cfd7556 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 14 Aug 2019 09:33:59 +0200 +Subject: [PATCH 21/23] backport the pam_authc_ppolicy option + +--- + man/nslcd.conf.5.xml | 12 ++++++++++++ + nslcd/cfg.c | 11 +++++++++++ + nslcd/cfg.h | 3 +++ + nslcd/myldap.c | 19 +++++++++++-------- + 4 files changed, 37 insertions(+), 8 deletions(-) + +diff --git a/man/nslcd.conf.5.xml b/man/nslcd.conf.5.xml +index d7fa9b8..7c2d45a 100644 +--- a/man/nslcd.conf.5.xml ++++ b/man/nslcd.conf.5.xml +@@ -733,6 +733,18 @@ + + + ++ ++ yes|no ++ ++ ++ This option specifies whether password policy controls are requested ++ and handled from the LDAP server when performing ++ user authentication. ++ By default the controls are requested and handled if available. ++ ++ ++ ++ + + + FILTER +diff --git a/nslcd/cfg.c b/nslcd/cfg.c +index b821fcd..e11d03a 100644 +--- a/nslcd/cfg.c ++++ b/nslcd/cfg.c +@@ -1205,6 +1205,17 @@ static void cfg_read(const char *filename,struct ldap_config *cfg) + { + parse_pam_password_prohibit_message_statement(filename,lnr,keyword,line,cfg); + } ++ else if (strcasecmp(keyword, "pam_authc_ppolicy") == 0) ++ { ++#if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE) ++ get_boolean(filename,lnr,keyword,&line,&cfg->pam_authc_ppolicy); ++ get_eol(filename, lnr, keyword, &line); ++#else ++ log_log(LOG_ERR, "%s:%d: value %s not supported on platform", ++ filename, lnr, value); ++ exit(EXIT_FAILURE); ++#endif ++ } + #ifdef ENABLE_CONFIGFILE_CHECKING + /* fallthrough */ + else +diff --git a/nslcd/cfg.h b/nslcd/cfg.h +index 5356ace..4c044ca 100644 +--- a/nslcd/cfg.h ++++ b/nslcd/cfg.h +@@ -156,6 +156,9 @@ struct ldap_config + /* whether password changing should be denied and user prompted with + this message */ + char *pam_password_prohibit_message; ++#if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE) ++ int pam_authc_ppolicy; /* whether to send password policy controls on bind */ ++#endif + }; + + /* this is a pointer to the global configuration, it should be available +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index 86a339e..738a782 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -522,18 +522,21 @@ static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri) + int rc, parserc; + struct berval cred; + LDAPControl passwd_policy_req; +- LDAPControl *requestctrls[2]; ++ LDAPControl *requestctrls[2] = { NULL, NULL }; + LDAPControl **responsectrls; + int msgid; + struct timeval timeout; + LDAPMessage *result; +- /* build password policy request control */ +- passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST; +- passwd_policy_req.ldctl_value.bv_val = NULL; /* none */ +- passwd_policy_req.ldctl_value.bv_len = 0; +- passwd_policy_req.ldctl_iscritical = 0; /* not critical */ +- requestctrls[0] = &passwd_policy_req; +- requestctrls[1] = NULL; ++ /* build policy request if pam_authc_ppolicy is set */ ++ if (nslcd_cfg->pam_authc_ppolicy) ++ { ++ passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST; ++ passwd_policy_req.ldctl_value.bv_val = NULL; /* none */ ++ passwd_policy_req.ldctl_value.bv_len = 0; ++ passwd_policy_req.ldctl_iscritical = 0; /* not critical */ ++ requestctrls[0] = &passwd_policy_req; ++ requestctrls[1] = NULL; ++ } + /* build password berval */ + cred.bv_val = (char *)session->bindpw; + cred.bv_len = (session->bindpw == NULL) ? 0 : strlen(session->bindpw); +-- +2.20.1 + diff --git a/SOURCES/0022-Backport-the-human-readable-password-policy-reply.patch b/SOURCES/0022-Backport-the-human-readable-password-policy-reply.patch new file mode 100644 index 0000000..e66141f --- /dev/null +++ b/SOURCES/0022-Backport-the-human-readable-password-policy-reply.patch @@ -0,0 +1,103 @@ +From d4849e30b0c27878ee1167784c82b15c371781a8 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 14 Aug 2019 09:39:53 +0200 +Subject: [PATCH 22/23] Backport the human readable password policy reply + +--- + nslcd/myldap.c | 66 ++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 59 insertions(+), 7 deletions(-) + +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index 738a782..f83137a 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -405,6 +405,63 @@ static int do_sasl_interact(LDAP UNUSED(*ld),unsigned UNUSED(flags),void *defaul + #endif /* HAVE_SASL_INTERACT_T */ + + #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE) ++static void print_ppolicy_expiry(MYLDAP_SESSION *session, unsigned int sec) ++{ ++ unsigned int days = 0; ++ unsigned int hours = 0; ++ unsigned int minutes = 0; ++ /* return this warning so PAM can present it to the user */ ++ if (strlen(session->policy_message) != 0) ++ return; ++ if (sec > 24 * 3600) ++ { ++ days = sec / (24 * 3600); ++ sec -= days * 24 * 3600; ++ } ++ if (sec > 3600) ++ { ++ hours = sec / 3600; ++ sec -= (hours * 3600); ++ } ++ if (sec > 60) ++ { ++ minutes = sec / 60; ++ sec -= minutes * 60; ++ } ++ if (days > 1) ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u days", days); ++ else if (days > 0) ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u hours", hours + 24); ++ else if (hours > 1) ++ { ++ if (minutes > 1) ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u hours and %u minutes", ++ hours, minutes); ++ else ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u hours", hours); ++ } ++ else if (hours > 0) ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u minutes", minutes + 60); ++ else if (minutes > 1) ++ { ++ if (sec > 1) ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u minutes and %u seconds", ++ minutes, sec); ++ else ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u minutes", minutes); ++ } ++ else ++ mysnprintf(session->policy_message, sizeof(session->policy_message), ++ "Password will expires in %u seconds", sec); ++} ++ + static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls) + { + int i; +@@ -434,11 +491,7 @@ static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRING (password will expire in %ld seconds)", + sec); + /* return this warning so PAM can present it to the user */ +- if (strlen(session->policy_message) == 0) +- { +- mysnprintf(session->policy_message, sizeof(session->policy_message), +- "password will expire in %ld seconds", sec); +- } ++ print_ppolicy_expiry(session, (unsigned int)sec); + } + else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) + { +@@ -502,8 +555,7 @@ static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPContr + { + /* if no other error has happened, this indicates that the password + will soon expire (number of seconds) */ +- mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expire in %d seconds", expire); ++ print_ppolicy_expiry(session, (unsigned int)expire); + } + else if ((grace >= 0) && (strlen(session->policy_message) == 0)) + { +-- +2.20.1 + diff --git a/SOURCES/0023-Backport-typo-fixes-in-password-expiration-warnings.patch b/SOURCES/0023-Backport-typo-fixes-in-password-expiration-warnings.patch new file mode 100644 index 0000000..2f8aa3f --- /dev/null +++ b/SOURCES/0023-Backport-typo-fixes-in-password-expiration-warnings.patch @@ -0,0 +1,61 @@ +From ddd068c82cb039ddb4531d4817f75252eef51332 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 14 Aug 2019 09:41:24 +0200 +Subject: [PATCH 23/23] Backport typo fixes in password expiration warnings + +--- + nslcd/myldap.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index f83137a..4b53af2 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -430,36 +430,36 @@ static void print_ppolicy_expiry(MYLDAP_SESSION *session, unsigned int sec) + } + if (days > 1) + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u days", days); ++ "Password will expire in %u days", days); + else if (days > 0) + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u hours", hours + 24); ++ "Password will expire in %u hours", hours + 24); + else if (hours > 1) + { + if (minutes > 1) + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u hours and %u minutes", ++ "Password will expire in %u hours and %u minutes", + hours, minutes); + else + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u hours", hours); ++ "Password will expire in %u hours", hours); + } + else if (hours > 0) + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u minutes", minutes + 60); ++ "Password will expire in %u minutes", minutes + 60); + else if (minutes > 1) + { + if (sec > 1) + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u minutes and %u seconds", ++ "Password will expire in %u minutes and %u seconds", + minutes, sec); + else + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u minutes", minutes); ++ "Password will expire in %u minutes", minutes); + } + else + mysnprintf(session->policy_message, sizeof(session->policy_message), +- "Password will expires in %u seconds", sec); ++ "Password will expire in %u seconds", sec); + } + + static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls) +-- +2.20.1 + diff --git a/SOURCES/0024-Allow-logging-longer-lines.patch b/SOURCES/0024-Allow-logging-longer-lines.patch new file mode 100644 index 0000000..ffadfda --- /dev/null +++ b/SOURCES/0024-Allow-logging-longer-lines.patch @@ -0,0 +1,27 @@ +From f50540857d5449ddb7dad31c99ba86de5bfeb4c6 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 13 Aug 2019 09:28:32 +0200 +Subject: [PATCH] Allow logging longer lines + +Resolves: +https://bugzilla.redhat.com/show_bug.cgi?id=1559524 +--- + nslcd/log.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/nslcd/log.c b/nslcd/log.c +index 27402f7..b4c0339 100644 +--- a/nslcd/log.c ++++ b/nslcd/log.c +@@ -122,7 +122,7 @@ void log_setrequest(const char *format, ...) + void log_log(int pri,const char *format, ...) + { + int res; +- char buffer[200]; ++ char buffer[512]; + va_list ap; + /* make the message */ + va_start(ap,format); +-- +2.20.1 + diff --git a/SOURCES/0025-Backport-of-Update-shadow.c-to-resolve-pwdLastSet-is.patch b/SOURCES/0025-Backport-of-Update-shadow.c-to-resolve-pwdLastSet-is.patch new file mode 100644 index 0000000..27851f9 --- /dev/null +++ b/SOURCES/0025-Backport-of-Update-shadow.c-to-resolve-pwdLastSet-is.patch @@ -0,0 +1,25 @@ +From 53f701c67021ddce1b554d2f85a96d9f28359bca Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 30 Aug 2019 12:52:42 +0200 +Subject: [PATCH] Backport of Update shadow.c to resolve pwdLastSet issue + +--- + nslcd/shadow.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/nslcd/shadow.c b/nslcd/shadow.c +index f7dee7d..46dacc9 100644 +--- a/nslcd/shadow.c ++++ b/nslcd/shadow.c +@@ -135,7 +135,7 @@ static long to_date(const char *dn,const char *date,const char *attr) + strncpy(buffer,date,l); + buffer[l]='\0'; + errno=0; +- value=strtol(date,&tmp,10); ++ value=strtol(buffer,&tmp,10); + if ((*date=='\0')||(*tmp!='\0')) + { + log_log(LOG_WARNING,"%s: %s: non-numeric",dn,attr); +-- +2.21.0 + diff --git a/SOURCES/0026-RHEL-specific-Disable-the-password-policies-unless-e.patch b/SOURCES/0026-RHEL-specific-Disable-the-password-policies-unless-e.patch new file mode 100644 index 0000000..a145243 --- /dev/null +++ b/SOURCES/0026-RHEL-specific-Disable-the-password-policies-unless-e.patch @@ -0,0 +1,45 @@ +From c9fb441600acc02952372fc89097cf06a8eaca5f Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 17 Sep 2019 09:48:49 +0200 +Subject: [PATCH] RHEL-specific: Disable the password policies unless + explicitly enabled + +--- + nslcd/cfg.c | 1 + + nslcd/myldap.c | 8 ++++++++ + 2 files changed, 9 insertions(+) + +diff --git a/nslcd/cfg.c b/nslcd/cfg.c +index e11d03a..66908b2 100644 +--- a/nslcd/cfg.c ++++ b/nslcd/cfg.c +@@ -136,6 +136,7 @@ static void cfg_defaults(struct ldap_config *cfg) + parse_validnames_statement(__FILE__,__LINE__,"", + "/^[a-z0-9._@$()]([a-z0-9._@$() \\~-]*[a-z0-9._@$()~-])?$/i",cfg); + cfg->pam_password_prohibit_message=NULL; ++ cfg->pam_authc_ppolicy = 0; + } + + /* simple strdup wrapper */ +diff --git a/nslcd/myldap.c b/nslcd/myldap.c +index 6eb91f8..a07829f 100644 +--- a/nslcd/myldap.c ++++ b/nslcd/myldap.c +@@ -689,6 +689,14 @@ static int do_bind(MYLDAP_SESSION *session, LDAP *ld,const char *binddn,const ch + if ((binddn!=NULL)&&(binddn[0]!='\0')) + { + #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE) ++ /* RHEL-specific: Don't even call into the ppolicy_bind path unless ++ * the option was set explicitly to 1 to retain backwards compatibility ++ */ ++ if (nslcd_cfg->pam_authc_ppolicy == 0) { ++ log_log(LOG_DEBUG,"ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",binddn, ++ ((bindpw!=NULL)&&(bindpw[0]!='\0'))?"\"***\"":"\"\"",uri); ++ return ldap_simple_bind_s(ld,binddn,bindpw); ++ } + return do_ppolicy_bind(session, ld, uri); + #else /* no SASL, so no ppolicy */ + /* do a simple bind */ +-- +2.21.0 + diff --git a/SOURCES/0027-RHEL-specific-document-the-ppolicy-option-default.patch b/SOURCES/0027-RHEL-specific-document-the-ppolicy-option-default.patch new file mode 100644 index 0000000..64f68f8 --- /dev/null +++ b/SOURCES/0027-RHEL-specific-document-the-ppolicy-option-default.patch @@ -0,0 +1,18 @@ +diff -up nss-pam-ldapd-0.8.13/man/nslcd.conf.5.ppolicy_option_default nss-pam-ldapd-0.8.13/man/nslcd.conf.5 +--- nss-pam-ldapd-0.8.13/man/nslcd.conf.5.ppolicy_option_default 2019-09-17 09:53:26.723676439 +0200 ++++ nss-pam-ldapd-0.8.13/man/nslcd.conf.5 2019-09-17 09:59:44.182750835 +0200 +@@ -360,6 +360,14 @@ vulnerabilities which allow denial of se + The default is to perform case-sensitve filtering of LDAP search + results for the above maps. + .TP ++\*(T<\fBpam_authc_ppolicy\fR\*(T> yes|no ++This option specifies whether password policy controls are requested ++and handled from the LDAP server when performing user authentication. ++ ++By default the controls are only requested and handled if this option ++is selected. This differs from the upstream default in order to retain ++backwards compatibility with previous RHEL-7 releases. ++.TP + \*(T<\fBpam_authz_search\fR\*(T> \fIFILTER\fR + This option allows flexible fine tuning of the authorisation check that + should be performed. The search filter specified is executed and diff --git a/SOURCES/nslcd.service b/SOURCES/nslcd.service index 61ae1fd..2fec365 100644 --- a/SOURCES/nslcd.service +++ b/SOURCES/nslcd.service @@ -1,6 +1,8 @@ [Unit] Description=Naming services LDAP client daemon. After=syslog.target network.target named.service dirsrv.target slapd.service +Wants=nss-user-lookup.target +Before=nss-user-lookup.target Documentation=man:nslcd(8) man:nslcd.conf(5) [Service] diff --git a/SPECS/nss-pam-ldapd.spec b/SPECS/nss-pam-ldapd.spec index a1c51e7..5f445d9 100644 --- a/SPECS/nss-pam-ldapd.spec +++ b/SPECS/nss-pam-ldapd.spec @@ -39,7 +39,7 @@ Name: nss-pam-ldapd Version: 0.8.13 -Release: 16%{?dist}.1 +Release: 22%{?dist} Summary: An nsswitch module which uses directory servers Group: System Environment/Base License: LGPLv2+ @@ -64,6 +64,21 @@ Patch12: nss-pam-ldapd-0.8.13-password-longer-than-64-chars.patch Patch13: nss-pam-ldapd-0.8.13-uri-man-fix.patch Patch14: nss-pam-ldapd-0.8.13-uid_formatting.patch Patch15: nss-pam-ldapd-bz1676861-Increase-size-of-config-file-token.patch +# rhbz#1612543 - Password expiration notification is not sent if the LDAP user doesn't have the objectClass shadowAccount. +Patch16: 0016-Backport-of-request-and-parse-password-policy-contro.patch +Patch17: 0017-Backport-of-passing-expiration-controls-back-to-PAM-.patch +Patch18: 0018-Also-extract-policy-controls-on-BIND-failure.patch +Patch19: 0019-Fix-password-policy-expiration-warnings.patch +Patch20: 0020-Simplify-password-policy-message-handling.patch +Patch21: 0021-backport-the-pam_authc_ppolicy-option.patch +Patch22: 0022-Backport-the-human-readable-password-policy-reply.patch +Patch23: 0023-Backport-typo-fixes-in-password-expiration-warnings.patch +Patch24: 0024-Allow-logging-longer-lines.patch +# rhbz#1618558 - AD authentication on RHEL using nslcd fails with error "pwdLastSet: password changed in the future" +Patch25: 0025-Backport-of-Update-shadow.c-to-resolve-pwdLastSet-is.patch +# rhbz#1612543 - Password expiration notification is not sent if the LDAP user doesn't have the objectClass shadowAccount. +Patch26: 0026-RHEL-specific-Disable-the-password-policies-unless-e.patch +Patch27: 0027-RHEL-specific-document-the-ppolicy-option-default.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: openldap-devel, krb5-devel @@ -126,6 +141,18 @@ nsswitch module. %patch13 -p1 -b .uri_list %patch14 -p1 -b .uid_formatting %patch15 -p1 -b .config_token_size +%patch16 -p1 -b .backport_policy_control_daemon +%patch17 -p1 -b .backport_policy_control_pam +%patch18 -p1 -b .ppolicy_bind_failure +%patch19 -p1 -b .fix_expiration_warnings +%patch20 -p1 -b .simplify_msg_handling +%patch21 -p1 -b .ppolicy_option +%patch22 -p1 -b .ppolicy_human_readable +%patch23 -p1 -b .ppolicy_typos +%patch24 -p1 -b .long_log_lines +%patch25 -p1 -b .pwd_last_set +%patch26 -p1 -b .ppolicy_default +%patch27 -p1 -b .ppolicy_default_man autoreconf -f -i %build @@ -362,8 +389,30 @@ exit 0 %endif %changelog -* Thu Mar 14 2019 Jakub Hrozek - 0.8.13-16.1 -- Resolves: rhbz#1689119 - nslcd fails to connect to ldap if fqdn is large [rhel-7.6.z] +* Tue Sep 17 2019 Jakub Hrozek - 0.8.13-22 +- Do not enable the password expiration controls by default +- Document the option and its default +- Resolves: rhbz#1612543 - Password expiration notification is not sent + if the LDAP user doesn't have the objectClass + shadowAccount. + +* Fri Aug 30 2019 Jakub Hrozek - 0.8.13-21 +- Resolves: rhbz#1618558 - AD authentication on RHEL using nslcd fails + with error "pwdLastSet: password changed in the + future" + +* Tue Aug 27 2019 Jakub Hrozek - 0.8.13-20 +- Resolves: rhbz#1714763 - [RHEL7] Correct startup dependencies for cgcred service for nslcd/LDAP clients + +* Thu Aug 22 2019 Jakub Hrozek - 0.8.13-19 +- Resolves: rhbz#1559524 - nslcd only prints 200 characters in error message + +* Wed Aug 21 2019 Jakub Hrozek - 0.8.13-18 +- Resolves: rhbz#1612543 - Password expiration notification is not sent if + the LDAP user doesn't have the objectClass shadowAccount. + +* Thu Mar 14 2019 Jakub Hrozek - 0.8.13-17 +- Resolves: rhbz#1676861 - nslcd fails to connect to ldap if fqdn is large * Tue Oct 24 2017 Jakub Hrozek - 0.8.13-16 - Resolves: rhbz#1151675 - NSLCD WRAPS LDAP USER UIDNUMBER > 2^31 SO UID