summaryrefslogtreecommitdiffstats
path: root/lib/auth/pam/pam.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/auth/pam/pam.c')
-rw-r--r--lib/auth/pam/pam.c443
1 files changed, 443 insertions, 0 deletions
diff --git a/lib/auth/pam/pam.c b/lib/auth/pam/pam.c
new file mode 100644
index 0000000..ed5071b
--- /dev/null
+++ b/lib/auth/pam/pam.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include<config.h>
+RCSID("$Id: pam.c 11417 2002-09-09 15:57:24Z joda $");
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <syslog.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#ifndef PAM_AUTHTOK_RECOVERY_ERR /* Fix linsux typo. */
+#define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
+#endif
+
+#include <netinet/in.h>
+#include <krb.h>
+#include <kafs.h>
+
+#if 0
+/* Debugging PAM modules is a royal pain, truss helps. */
+#define DEBUG(msg) (access(msg " at line", __LINE__))
+#endif
+
+static void
+psyslog(int level, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ openlog("pam_krb4", LOG_PID, LOG_AUTH);
+ vsyslog(level, format, args);
+ va_end(args);
+ closelog();
+}
+
+enum {
+ KRB4_DEBUG,
+ KRB4_USE_FIRST_PASS,
+ KRB4_TRY_FIRST_PASS,
+ KRB4_IGNORE_ROOT,
+ KRB4_NO_VERIFY,
+ KRB4_REAFSLOG,
+ KRB4_CTRLS /* Number of ctrl arguments defined. */
+};
+
+#define KRB4_DEFAULTS 0
+
+static int ctrl_flags = KRB4_DEFAULTS;
+#define ctrl_on(x) (krb4_args[x].flag & ctrl_flags)
+#define ctrl_off(x) (!ctrl_on(x))
+
+typedef struct
+{
+ const char *token;
+ unsigned int flag;
+} krb4_ctrls_t;
+
+static krb4_ctrls_t krb4_args[KRB4_CTRLS] =
+{
+ /* KRB4_DEBUG */ { "debug", 0x01 },
+ /* KRB4_USE_FIRST_PASS */ { "use_first_pass", 0x02 },
+ /* KRB4_TRY_FIRST_PASS */ { "try_first_pass", 0x04 },
+ /* KRB4_IGNORE_ROOT */ { "ignore_root", 0x08 },
+ /* KRB4_NO_VERIFY */ { "no_verify", 0x10 },
+ /* KRB4_REAFSLOG */ { "reafslog", 0x20 },
+};
+
+static void
+parse_ctrl(int argc, const char **argv)
+{
+ int i, j;
+
+ ctrl_flags = KRB4_DEFAULTS;
+ for (i = 0; i < argc; i++)
+ {
+ for (j = 0; j < KRB4_CTRLS; j++)
+ if (strcmp(argv[i], krb4_args[j].token) == 0)
+ break;
+
+ if (j >= KRB4_CTRLS)
+ psyslog(LOG_ALERT, "unrecognized option [%s]", *argv);
+ else
+ ctrl_flags |= krb4_args[j].flag;
+ }
+}
+
+static void
+pdeb(const char *format, ...)
+{
+ va_list args;
+ if (ctrl_off(KRB4_DEBUG))
+ return;
+ va_start(args, format);
+ openlog("pam_krb4", LOG_PID, LOG_AUTH);
+ vsyslog(LOG_DEBUG, format, args);
+ va_end(args);
+ closelog();
+}
+
+#define ENTRY(func) pdeb("%s() flags = %d ruid = %d euid = %d", func, flags, getuid(), geteuid())
+
+static void
+set_tkt_string(uid_t uid)
+{
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "%s%u", TKT_ROOT, (unsigned)uid);
+ krb_set_tkt_string(buf);
+
+#if 0
+ /* pam_set_data+pam_get_data are not guaranteed to work, grr. */
+ pam_set_data(pamh, "KRBTKFILE", strdup(t), cleanup);
+ if (pam_get_data(pamh, "KRBTKFILE", (const void**)&tkt) == PAM_SUCCESS)
+ {
+ pam_putenv(pamh, var);
+ }
+#endif
+
+ /* We don't want to inherit this variable.
+ * If we still do, it must have a sane value. */
+ if (getenv("KRBTKFILE") != 0)
+ {
+ char *var = malloc(sizeof(buf));
+ snprintf(var, sizeof(buf), "KRBTKFILE=%s", tkt_string());
+ putenv(var);
+ /* free(var); XXX */
+ }
+}
+
+static int
+verify_pass(pam_handle_t *pamh,
+ const char *name,
+ const char *inst,
+ const char *pass)
+{
+ char realm[REALM_SZ];
+ int ret, krb_verify, old_euid, old_ruid;
+
+ krb_get_lrealm(realm, 1);
+ if (ctrl_on(KRB4_NO_VERIFY))
+ krb_verify = KRB_VERIFY_SECURE_FAIL;
+ else
+ krb_verify = KRB_VERIFY_SECURE;
+ old_ruid = getuid();
+ old_euid = geteuid();
+ setreuid(0, 0);
+ ret = krb_verify_user(name, inst, realm, pass, krb_verify, NULL);
+ pdeb("krb_verify_user(`%s', `%s', `%s', pw, %d, NULL) returns %s",
+ name, inst, realm, krb_verify,
+ krb_get_err_text(ret));
+ setreuid(old_ruid, old_euid);
+ if (getuid() != old_ruid || geteuid() != old_euid)
+ {
+ psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
+ old_ruid, old_euid, __LINE__);
+ exit(1);
+ }
+
+ switch(ret) {
+ case KSUCCESS:
+ return PAM_SUCCESS;
+ case KDC_PR_UNKNOWN:
+ return PAM_USER_UNKNOWN;
+ case SKDC_CANT:
+ case SKDC_RETRY:
+ case RD_AP_TIME:
+ return PAM_AUTHINFO_UNAVAIL;
+ default:
+ return PAM_AUTH_ERR;
+ }
+}
+
+static int
+krb4_auth(pam_handle_t *pamh,
+ int flags,
+ const char *name,
+ const char *inst,
+ struct pam_conv *conv)
+{
+ struct pam_response *resp;
+ char prompt[128];
+ struct pam_message msg, *pmsg = &msg;
+ int ret;
+
+ if (ctrl_on(KRB4_TRY_FIRST_PASS) || ctrl_on(KRB4_USE_FIRST_PASS))
+ {
+ char *pass = 0;
+ ret = pam_get_item(pamh, PAM_AUTHTOK, (void **) &pass);
+ if (ret != PAM_SUCCESS)
+ {
+ psyslog(LOG_ERR , "pam_get_item returned error to get-password");
+ return ret;
+ }
+ else if (pass != 0 && verify_pass(pamh, name, inst, pass) == PAM_SUCCESS)
+ return PAM_SUCCESS;
+ else if (ctrl_on(KRB4_USE_FIRST_PASS))
+ return PAM_AUTHTOK_RECOVERY_ERR; /* Wrong password! */
+ else
+ /* We tried the first password but it didn't work, cont. */;
+ }
+
+ msg.msg_style = PAM_PROMPT_ECHO_OFF;
+ if (*inst == 0)
+ snprintf(prompt, sizeof(prompt), "%s's Password: ", name);
+ else
+ snprintf(prompt, sizeof(prompt), "%s.%s's Password: ", name, inst);
+ msg.msg = prompt;
+
+ ret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr);
+ if (ret != PAM_SUCCESS)
+ return ret;
+
+ ret = verify_pass(pamh, name, inst, resp->resp);
+ if (ret == PAM_SUCCESS)
+ {
+ memset(resp->resp, 0, strlen(resp->resp)); /* Erase password! */
+ free(resp->resp);
+ free(resp);
+ }
+ else
+ {
+ pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* Save password. */
+ /* free(resp->resp); XXX */
+ /* free(resp); XXX */
+ }
+
+ return ret;
+}
+
+int
+pam_sm_authenticate(pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ char *user;
+ int ret;
+ struct pam_conv *conv;
+ struct passwd *pw;
+ uid_t uid = -1;
+ const char *name, *inst;
+ char realm[REALM_SZ];
+ realm[0] = 0;
+
+ parse_ctrl(argc, argv);
+ ENTRY("pam_sm_authenticate");
+
+ ret = pam_get_user(pamh, &user, "login: ");
+ if (ret != PAM_SUCCESS)
+ return ret;
+
+ if (ctrl_on(KRB4_IGNORE_ROOT) && strcmp(user, "root") == 0)
+ return PAM_AUTHINFO_UNAVAIL;
+
+ ret = pam_get_item(pamh, PAM_CONV, (void*)&conv);
+ if (ret != PAM_SUCCESS)
+ return ret;
+
+ pw = getpwnam(user);
+ if (pw != 0)
+ {
+ uid = pw->pw_uid;
+ set_tkt_string(uid);
+ }
+
+ if (strcmp(user, "root") == 0 && getuid() != 0)
+ {
+ pw = getpwuid(getuid());
+ if (pw != 0)
+ {
+ name = strdup(pw->pw_name);
+ inst = "root";
+ }
+ }
+ else
+ {
+ name = user;
+ inst = "";
+ }
+
+ ret = krb4_auth(pamh, flags, name, inst, conv);
+
+ /*
+ * The realm was lost inside krb_verify_user() so we can't simply do
+ * a krb_kuserok() when inst != "".
+ */
+ if (ret == PAM_SUCCESS && inst[0] != 0)
+ {
+ uid_t old_euid = geteuid();
+ uid_t old_ruid = getuid();
+
+ setreuid(0, 0); /* To read ticket file. */
+ if (krb_get_tf_fullname(tkt_string(), 0, 0, realm) != KSUCCESS)
+ ret = PAM_SERVICE_ERR;
+ else if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
+ {
+ setreuid(0, uid); /* To read ~/.klogin. */
+ if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
+ ret = PAM_PERM_DENIED;
+ }
+
+ if (ret != PAM_SUCCESS)
+ {
+ dest_tkt(); /* Passwd known, ok to kill ticket. */
+ psyslog(LOG_NOTICE,
+ "%s.%s@%s is not allowed to log in as %s",
+ name, inst, realm, user);
+ }
+
+ setreuid(old_ruid, old_euid);
+ if (getuid() != old_ruid || geteuid() != old_euid)
+ {
+ psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
+ old_ruid, old_euid, __LINE__);
+ exit(1);
+ }
+ }
+
+ if (ret == PAM_SUCCESS)
+ {
+ psyslog(LOG_INFO,
+ "%s.%s@%s authenticated as user %s",
+ name, inst, realm, user);
+ if (chown(tkt_string(), uid, -1) == -1)
+ {
+ dest_tkt();
+ psyslog(LOG_ALERT , "chown(%s, %d, -1) failed", tkt_string(), uid);
+ exit(1);
+ }
+ }
+
+ /*
+ * Kludge alert!!! Sun dtlogin unlock screen fails to call
+ * pam_setcred(3) with PAM_REFRESH_CRED after a successful
+ * authentication attempt, sic.
+ *
+ * This hack is designed as a workaround to that problem.
+ */
+ if (ctrl_on(KRB4_REAFSLOG))
+ if (ret == PAM_SUCCESS)
+ pam_sm_setcred(pamh, PAM_REFRESH_CRED, argc, argv);
+
+ return ret;
+}
+
+int
+pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ parse_ctrl(argc, argv);
+ ENTRY("pam_sm_setcred");
+
+ switch (flags & ~PAM_SILENT) {
+ case 0:
+ case PAM_ESTABLISH_CRED:
+ if (k_hasafs())
+ k_setpag();
+ /* Fall through, fill PAG with credentials below. */
+ case PAM_REINITIALIZE_CRED:
+ case PAM_REFRESH_CRED:
+ if (k_hasafs())
+ {
+ void *user = 0;
+
+ if (pam_get_item(pamh, PAM_USER, &user) == PAM_SUCCESS)
+ {
+ struct passwd *pw = getpwnam((char *)user);
+ if (pw != 0)
+ krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0,
+ pw->pw_uid, pw->pw_dir);
+ }
+ }
+ break;
+ case PAM_DELETE_CRED:
+ dest_tkt();
+ if (k_hasafs())
+ k_unlog();
+ break;
+ default:
+ psyslog(LOG_ALERT , "pam_sm_setcred: unknown flags 0x%x", flags);
+ break;
+ }
+
+ return PAM_SUCCESS;
+}
+
+int
+pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ parse_ctrl(argc, argv);
+ ENTRY("pam_sm_open_session");
+
+ return PAM_SUCCESS;
+}
+
+
+int
+pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char**argv)
+{
+ parse_ctrl(argc, argv);
+ ENTRY("pam_sm_close_session");
+
+ /* This isn't really kosher, but it's handy. */
+ pam_sm_setcred(pamh, PAM_DELETE_CRED, argc, argv);
+
+ return PAM_SUCCESS;
+}
OpenPOWER on IntegriCloud