/*
* Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
* Copyright (C) 1996.
- * Copyright (c) Jan Rêkorajski, 1999.
+ * Copyright (c) Jan Rêkorajski, 1999.
* Copyright (c) Red Hat, Inc., 2007, 2008.
*
* Redistribution and use in source and binary forms, with or without
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
-#include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
-#include <rpcsvc/ypclnt.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
-#ifdef WITH_SELINUX
-static int selinux_enabled=-1;
-#include <selinux/selinux.h>
-#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
-#endif
+#include <sys/resource.h>
#include <security/_pam_macros.h>
/* indicate the following groups are defined */
-#define PAM_SM_PASSWORD
+#ifdef PAM_STATIC
+# include "pam_unix_static.h"
+#else
+# define PAM_SM_PASSWORD
+#endif
#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include <security/pam_modutil.h>
-#include "yppasswd.h"
#include "md5.h"
#include "support.h"
#include "passverify.h"
#include "bigcrypt.h"
-#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
+#if (HAVE_YP_GET_DEFAULT_DOMAIN || HAVE_GETDOMAINNAME) && HAVE_YP_MASTER
+# define HAVE_NIS
+#endif
+
+#ifdef HAVE_NIS
+# include <rpc/rpc.h>
+
+# if HAVE_RPCSVC_YP_PROT_H
+# include <rpcsvc/yp_prot.h>
+# endif
+
+# if HAVE_RPCSVC_YPCLNT_H
+# include <rpcsvc/ypclnt.h>
+# endif
+
+# include "yppasswd.h"
+
+# if !HAVE_DECL_GETRPCPORT
extern int getrpcport(const char *host, unsigned long prognum,
unsigned long versnum, unsigned int proto);
-#endif /* GNU libc 2.1 */
+# endif /* GNU libc 2.1 */
+#endif
/*
How it works:
#define MAX_PASSWD_TRIES 3
-static char *getNISserver(pam_handle_t *pamh)
+#ifdef HAVE_NIS
+static char *getNISserver(pam_handle_t *pamh, unsigned int ctrl)
{
char *master;
char *domainname;
int port, err;
+#ifdef HAVE_YP_GET_DEFAULT_DOMAIN
if ((err = yp_get_default_domain(&domainname)) != 0) {
pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
yperr_string(err));
return NULL;
}
+#elif defined(HAVE_GETDOMAINNAME)
+ char domainname_res[256];
+
+ if (getdomainname (domainname_res, sizeof (domainname_res)) == 0)
+ {
+ if (strcmp (domainname_res, "(none)") == 0)
+ {
+ /* If domainname is not set, some systems will return "(none)" */
+ domainname_res[0] = '\0';
+ }
+ domainname = domainname_res;
+ }
+ else domainname = NULL;
+#endif
+
if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
yperr_string(err));
"yppasswd daemon running on illegal port");
return NULL;
}
+ if (on(UNIX_DEBUG, ctrl)) {
+ pam_syslog(pamh, LOG_DEBUG, "Use NIS server on %s with port %d",
+ master, port);
+ }
return master;
}
+#endif
#ifdef WITH_SELINUX
const char *fromwhat, const char *towhat, int remember)
{
int retval, child, fds[2];
- void (*sighandler)(int) = NULL;
+ struct sigaction newsa, oldsa;
D(("called."));
/* create a pipe for the password */
* The "noreap" module argument is provided so that the admin can
* override this behavior.
*/
- sighandler = signal(SIGCHLD, SIG_DFL);
+ memset(&newsa, '\0', sizeof(newsa));
+ newsa.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &newsa, &oldsa);
}
/* fork */
rlim.rlim_max = MAX_FD_NO;
for (i=0; i < (int)rlim.rlim_max; i++) {
if (i != STDIN_FILENO)
- close(i);
+ close(i);
}
}
- if (SELINUX_ENABLED && geteuid() == 0) {
- /* must set the real uid to 0 so the helper will not error
- out if pam is called from setuid binary (su, sudo...) */
- setuid(0);
- }
-
/* exec binary helper */
args[0] = x_strdup(UPDATE_HELPER);
args[1] = x_strdup(user);
snprintf(buffer, sizeof(buffer), "%d", remember);
args[4] = x_strdup(buffer);
-
+
execve(UPDATE_HELPER, args, envp);
/* should not get here: exit with error */
D(("helper binary is not available"));
- exit(PAM_AUTHINFO_UNAVAIL);
+ _exit(PAM_AUTHINFO_UNAVAIL);
} else if (child > 0) {
/* wait for child */
/* if the stored password is NULL */
close(fds[0]); /* close here to avoid possible SIGPIPE above */
close(fds[1]);
- rc=waitpid(child, &retval, 0); /* wait for helper to complete */
+ /* wait for helper to complete: */
+ while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
if (rc<0) {
pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
- retval = PAM_AUTH_ERR;
- } else {
+ retval = PAM_AUTHTOK_ERR;
+ } else if (!WIFEXITED(retval)) {
+ pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval);
+ retval = PAM_AUTHTOK_ERR;
+ } else {
retval = WEXITSTATUS(retval);
}
} else {
D(("fork failed"));
close(fds[0]);
- close(fds[1]);
+ close(fds[1]);
retval = PAM_AUTH_ERR;
}
- if (sighandler != SIG_ERR) {
- (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
+ if (off(UNIX_NOREAP, ctrl)) {
+ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
}
return retval;
char *s_luser, *s_uid, *s_npas, *s_pas;
int retval = PAM_SUCCESS;
FILE *opwfile;
+ size_t len = strlen(forwho);
opwfile = fopen(OLD_PASSWORDS_FILE, "r");
if (opwfile == NULL)
return PAM_ABORT;
while (fgets(buf, 16380, opwfile)) {
- if (!strncmp(buf, forwho, strlen(forwho))) {
+ if (!strncmp(buf, forwho, len) && (buf[len] == ':' ||
+ buf[len] == ',')) {
char *sptr;
buf[strlen(buf) - 1] = '\0';
s_luser = strtok_r(buf, ":,", &sptr);
}
if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
- if ((master=getNISserver(pamh)) != NULL) {
+#ifdef HAVE_NIS
+ if ((master=getNISserver(pamh, ctrl)) != NULL) {
struct timeval timeout;
struct yppasswd yppwd;
CLIENT *clnt;
_("NIS password could not be changed."));
retval = PAM_TRY_AGAIN;
}
-#ifdef DEBUG
+#ifdef PAM_DEBUG
sleep(5);
#endif
} else {
retval = PAM_TRY_AGAIN;
}
+#else
+ if (on(UNIX_DEBUG, ctrl)) {
+ pam_syslog(pamh, LOG_DEBUG, "No NIS support available");
+ }
+
+ retval = PAM_TRY_AGAIN;
+#endif
}
if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
static int _pam_unix_approve_pass(pam_handle_t * pamh
,unsigned int ctrl
,const char *pass_old
- ,const char *pass_new)
+ ,const char *pass_new,
+ int pass_min_len)
{
const void *user;
const char *remark = NULL;
}
}
if (off(UNIX__IAMROOT, ctrl)) {
- if (strlen(pass_new) < 6)
+ if (strlen(pass_new) < pass_min_len)
remark = _("You must choose a longer password");
D(("length check [%s]", remark));
if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
return retval;
}
-
-PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
- int argc, const char **argv)
+int
+pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
unsigned int ctrl, lctrl;
int retval;
int remember = -1;
int rounds = -1;
+ int pass_min_len = 0;
/* <DO NOT free() THESE> */
const char *user;
D(("called."));
- ctrl = _set_ctrl(pamh, flags, &remember, &rounds, argc, argv);
+ ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len,
+ argc, argv);
/*
* First get the name of a user
if (*(const char *)pass_new == '\0') { /* "\0" password = NULL */
pass_new = NULL;
}
- retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
-
+ retval = _pam_unix_approve_pass(pamh, ctrl, pass_old,
+ pass_new, pass_min_len);
+
if (retval != PAM_SUCCESS && off(UNIX_NOT_SET_PASS, ctrl)) {
pam_set_item(pamh, PAM_AUTHTOK, NULL);
}
return retval;
}
- retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+ retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new,
+ pass_min_len);
if (retval != PAM_SUCCESS) {
pam_syslog(pamh, LOG_NOTICE,
"new password not acceptable 2");
tpass = create_password_hash(pamh, pass_new, ctrl, rounds);
if (tpass == NULL) {
pam_syslog(pamh, LOG_CRIT,
- "out of memory for password");
+ "crypt() failure or out of memory for password");
pass_new = pass_old = NULL; /* tidy up */
unlock_pwdf();
return PAM_BUF_ERR;
return retval;
}
-
-
-/* static module data */
-#ifdef PAM_STATIC
-struct pam_module _pam_unix_passwd_modstruct = {
- "pam_unix_passwd",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- pam_sm_chauthtok,
-};
-#endif