* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
+ *
+ * Some PAM resources:
+ *
+ * PAM home page:
+ * http://www.us.kernel.org/pub/linux/libs/pam/
+ *
+ * PAM FAQ:
+ * http://www.us.kernel.org/pub/linux/libs/pam/FAQ
+ *
+ * PAM Application Developers' Guide:
+ * http://www.us.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam_appl.html
+ *
+ * PAM Mailing list archives:
+ * http://www.linuxhq.com/lnxlists/linux-pam/
+ *
+ * Compatibility notes, especially between Linux and Solaris:
+ * http://www.contrib.andrew.cmu.edu/u/shadow/pam.html
+ *
+ * The Open Group's PAM API documentation:
+ * http://www.opengroup.org/onlinepubs/8329799/pam_start.htm
*/
#ifdef HAVE_CONFIG_H
/* We handle delays ourself.*/
/* Don't set this to 0 (Linux bug workaround.) */
# define PAM_NO_DELAY(pamh) pam_fail_delay ((pamh), 1)
-# else /* !HAVE_PAM_FAIL_DELAY */
+#else /* !HAVE_PAM_FAIL_DELAY */
# define PAM_NO_DELAY(pamh) /* */
-# endif /* !HAVE_PAM_FAIL_DELAY */
+#endif /* !HAVE_PAM_FAIL_DELAY */
+
+
+/* On SunOS 5.6, and on Linux with PAM 0.64, pam_strerror() takes two args.
+ On some other Linux systems with some other version of PAM (e.g.,
+ whichever Debian release comes with a 2.2.5 kernel) it takes one arg.
+ I can't tell which is more "recent" or "correct" behavior, so configure
+ figures out which is in use for us. Shoot me!
+ */
+#ifdef PAM_STRERROR_TWO_ARGS
+# define PAM_STRERROR(pamh, status) pam_strerror((pamh), (status))
+#else /* !PAM_STRERROR_TWO_ARGS */
+# define PAM_STRERROR(pamh, status) pam_strerror((status))
+#endif /* !PAM_STRERROR_TWO_ARGS */
+
/* PAM sucks in that there is no way to tell whether a particular service
is configured at all. That is, there is no way to tell the difference
*/
+/* On SunOS 5.6, the `pam_conv.appdata_ptr' slot seems to be ignored, and
+ the `closure' argument to pc.conv always comes in as random garbage.
+ So we get around this by using a global variable instead. Shoot me!
+ */
+static void *suns_pam_implementation_blows = 0;
+
+
/* This can be called at any time, and says whether the typed password
belongs to either the logged in user (real uid, not effective); or
to root.
pc.conv = &pam_conversation;
pc.appdata_ptr = (void *) &c;
+ /* On SunOS 5.6, the `appdata_ptr' slot seems to be ignored, and the
+ `closure' argument to pc.conv always comes in as random garbage. */
+ suns_pam_implementation_blows = (void *) &c;
+
/* Initialize PAM.
*/
if (verbose_p)
fprintf (stderr, "%s: pam_start (\"%s\", \"%s\", ...) ==> %d (%s)\n",
blurb(), service, c.user,
- status, pam_strerror (pamh, status));
+ status, PAM_STRERROR (pamh, status));
if (status != PAM_SUCCESS) goto DONE;
/* #### We should set PAM_TTY to the display we're using, but we
status = pam_set_item (pamh, PAM_TTY, strdup(tty));
if (verbose_p)
fprintf (stderr, "%s: pam_set_item (p, PAM_TTY, \"%s\") ==> %d (%s)\n",
- blurb(), tty, status, pam_strerror(pamh, status));
+ blurb(), tty, status, PAM_STRERROR(pamh, status));
}
/* Try to authenticate as the current user.
status = pam_authenticate (pamh, 0);
if (verbose_p)
fprintf (stderr, "%s: pam_authenticate (...) ==> %d (%s)\n",
- blurb(), status, pam_strerror(pamh, status));
+ blurb(), status, PAM_STRERROR(pamh, status));
if (status == PAM_SUCCESS) /* Win! */
goto DONE;
status = pam_set_item (pamh, PAM_USER, strdup(c.user));
if (verbose_p)
fprintf (stderr, "%s: pam_set_item(p, PAM_USER, \"%s\") ==> %d (%s)\n",
- blurb(), c.user, status, pam_strerror(pamh, status));
+ blurb(), c.user, status, PAM_STRERROR(pamh, status));
if (status != PAM_SUCCESS) goto DONE;
PAM_NO_DELAY(pamh);
status = pam_authenticate (pamh, 0);
if (verbose_p)
fprintf (stderr, "%s: pam_authenticate (...) ==> %d (%s)\n",
- blurb(), status, pam_strerror(pamh, status));
+ blurb(), status, PAM_STRERROR(pamh, status));
DONE:
if (user) free (user);
Bool
-pam_lock_init (int argc, char **argv, Bool verbose_p)
+pam_priv_init (int argc, char **argv, Bool verbose_p)
{
/* We have nothing to do at init-time.
However, we might as well do some error checking.
If "/etc/pam.d" exists and is a directory, but "/etc/pam.d/xlock"
does not exist, warn that PAM probably isn't going to work.
+
+ This is a priv-init instead of a non-priv init in case the directory
+ is unreadable or something (don't know if that actually happens.)
*/
- const char dir[] = "/etc/pam.d";
- const char file[] = "/etc/pam.d/" PAM_SERVICE_NAME;
+ const char dir[] = "/etc/pam.d";
+ const char file[] = "/etc/pam.d/" PAM_SERVICE_NAME;
+ const char file2[] = "/etc/pam.conf";
struct stat st;
+
if (stat (dir, &st) == 0 && st.st_mode & S_IFDIR)
- if (stat (file, &st) != 0)
+ {
+ if (stat (file, &st) != 0)
+ fprintf (stderr,
+ "%s: warning: %s does not exist.\n"
+ "%s: password authentication via PAM is unlikely to work.\n",
+ blurb(), file, blurb());
+ }
+ else if (stat (file2, &st) == 0)
+ {
+ FILE *f = fopen (file2, "r");
+ if (f)
+ {
+ Bool ok = False;
+ char buf[255];
+ while (fgets (buf, sizeof(buf), f))
+ if (strstr (buf, PAM_SERVICE_NAME))
+ {
+ ok = True;
+ break;
+ }
+ fclose (f);
+ if (!ok)
+ {
+ fprintf (stderr,
+ "%s: warning: %s does not list the `%s' service.\n"
+ "%s: password authentication via PAM is unlikely to work.\n",
+ blurb(), file2, PAM_SERVICE_NAME, blurb());
+ }
+ }
+ /* else warn about file2 existing but being unreadable? */
+ }
+ else
+ {
fprintf (stderr,
- "%s: warning: %s does not exist.\n"
+ "%s: warning: neither %s nor %s exist.\n"
"%s: password authentication via PAM is unlikely to work.\n",
- blurb(), file, blurb());
+ blurb(), file2, file, blurb());
+ }
/* Return true anyway, just in case. */
return True;
struct pam_response *reply = 0;
struct pam_closure *c = (struct pam_closure *) closure;
+ /* On SunOS 5.6, the `closure' argument always comes in as random garbage. */
+ c = (struct pam_closure *) suns_pam_implementation_blows;
+
+
reply = (struct pam_response *) calloc (nmsgs, sizeof (*reply));
if (!reply) return PAM_CONV_ERR;