diff options
author | Steve Langasek <steve.langasek@ubuntu.com> | 2019-01-03 12:48:14 -0800 |
---|---|---|
committer | Steve Langasek <steve.langasek@ubuntu.com> | 2019-01-03 12:48:14 -0800 |
commit | d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c (patch) | |
tree | ba5654cffacfd2002eefc5bc3764a7971afff1dc /Linux-PAM/modules/pam_lastlog/pam_lastlog.c | |
parent | 4c51da22e068907adb7857d50f5109a467c94d7c (diff) | |
parent | 7cbfa335c57d068d59508c844f3957165cccfb9b (diff) | |
download | pam-d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c.tar.gz pam-d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c.tar.bz2 pam-d5b06b67bbeeed7c05c0eb2e05d6a972ad050d1c.zip |
New upstream version 0.99.7.1
Diffstat (limited to 'Linux-PAM/modules/pam_lastlog/pam_lastlog.c')
-rw-r--r-- | Linux-PAM/modules/pam_lastlog/pam_lastlog.c | 510 |
1 files changed, 246 insertions, 264 deletions
diff --git a/Linux-PAM/modules/pam_lastlog/pam_lastlog.c b/Linux-PAM/modules/pam_lastlog/pam_lastlog.c index c9c5e24e..a75e1ce7 100644 --- a/Linux-PAM/modules/pam_lastlog/pam_lastlog.c +++ b/Linux-PAM/modules/pam_lastlog/pam_lastlog.c @@ -1,8 +1,6 @@ /* pam_lastlog module */ /* - * $Id: pam_lastlog.c,v 1.8 2004/09/24 13:13:20 kukuk Exp $ - * * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11 * * This module does the necessary work to display the last login @@ -10,10 +8,11 @@ * present (login) service. */ -#include <security/_pam_aconf.h> +#include "config.h" #include <fcntl.h> #include <time.h> +#include <errno.h> #ifdef HAVE_UTMP_H # include <utmp.h> #else @@ -28,10 +27,6 @@ #include <syslog.h> #include <unistd.h> -#ifdef WANT_PWDB -#include <pwdb/pwdb_public.h> /* use POSIX front end */ -#endif - #if defined(hpux) || defined(sunos) || defined(solaris) # ifndef _PATH_LASTLOG # define _PATH_LASTLOG "/usr/adm/lastlog" @@ -56,17 +51,6 @@ struct lastlog { #define DEFAULT_HOST "" /* "[no.where]" */ #define DEFAULT_TERM "" /* "tt???" */ -#define LASTLOG_NEVER_WELCOME "Welcome to your new account!" -#define LASTLOG_INTRO "Last login:" -#define LASTLOG_TIME " %s" -#define _LASTLOG_HOST_FORMAT " from %%.%ds" -#define _LASTLOG_LINE_FORMAT " on %%.%ds" -#define LASTLOG_TAIL "" -#define LASTLOG_MAXSIZE (sizeof(LASTLOG_INTRO)+0 \ - +sizeof(LASTLOG_TIME)+strlen(the_time) \ - +sizeof(_LASTLOG_HOST_FORMAT)+UT_HOSTSIZE \ - +sizeof(_LASTLOG_LINE_FORMAT)+UT_LINESIZE \ - +sizeof(LASTLOG_TAIL)) /* * here, we make a definition for the externally accessible function @@ -79,20 +63,8 @@ struct lastlog { #include <security/pam_modules.h> #include <security/_pam_macros.h> -#include <security/_pam_modutil.h> - -/* some syslogging */ - -static void _log_err(int err, const char *format, ...) -{ - va_list args; - - va_start(args, format); - openlog("PAM-lastlog", LOG_CONS|LOG_PID, LOG_AUTH); - vsyslog(err, format, args); - va_end(args); - closelog(); -} +#include <security/pam_modutil.h> +#include <security/pam_ext.h> /* argument parsing */ @@ -102,10 +74,12 @@ static void _log_err(int err, const char *format, ...) #define LASTLOG_NEVER 010 /* display a welcome message for first login */ #define LASTLOG_DEBUG 020 /* send info to syslog(3) */ #define LASTLOG_QUIET 040 /* keep quiet about things */ +#define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */ -static int _pam_parse(int flags, int argc, const char **argv) +static int +_pam_parse(pam_handle_t *pamh, int flags, int argc, const char **argv) { - int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE); + int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP); /* does the appliction require quiet? */ if (flags & PAM_SILENT) { @@ -120,17 +94,19 @@ static int _pam_parse(int flags, int argc, const char **argv) if (!strcmp(*argv,"debug")) { ctrl |= LASTLOG_DEBUG; } else if (!strcmp(*argv,"nodate")) { - ctrl |= ~LASTLOG_DATE; + ctrl &= ~LASTLOG_DATE; } else if (!strcmp(*argv,"noterm")) { - ctrl |= ~LASTLOG_LINE; + ctrl &= ~LASTLOG_LINE; } else if (!strcmp(*argv,"nohost")) { - ctrl |= ~LASTLOG_HOST; + ctrl &= ~LASTLOG_HOST; } else if (!strcmp(*argv,"silent")) { ctrl |= LASTLOG_QUIET; } else if (!strcmp(*argv,"never")) { ctrl |= LASTLOG_NEVER; + } else if (!strcmp(*argv,"nowtmp")) { + ctrl &= ~LASTLOG_WTMP; } else { - _log_err(LOG_ERR,"unknown option; %s",*argv); + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); } } @@ -138,273 +114,269 @@ static int _pam_parse(int flags, int argc, const char **argv) return ctrl; } -/* a front end for conversations */ - -static int converse(pam_handle_t *pamh, int ctrl, int nargs - , struct pam_message **message - , struct pam_response **response) +static const char * +get_tty(pam_handle_t *pamh) { - int retval; - struct pam_conv *conv; + const void *void_terminal_line = NULL; + const char *terminal_line; - D(("begin to converse")); + if (pam_get_item(pamh, PAM_TTY, &void_terminal_line) != PAM_SUCCESS + || void_terminal_line == NULL) { + terminal_line = DEFAULT_TERM; + } else { + terminal_line = void_terminal_line; + } + if (!strncmp("/dev/", terminal_line, 5)) { + /* strip leading "/dev/" from tty. */ + terminal_line += 5; + } + D(("terminal = %s", terminal_line)); + return terminal_line; +} - retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; - if ( retval == PAM_SUCCESS && conv) { +static int +last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid) +{ + struct flock last_lock; + struct lastlog last_login; + int retval = PAM_SUCCESS; + char the_time[256]; + char *date = NULL; + char *host = NULL; + char *line = NULL; + + memset(&last_lock, 0, sizeof(last_lock)); + last_lock.l_type = F_RDLCK; + last_lock.l_whence = SEEK_SET; + last_lock.l_start = sizeof(last_login) * (off_t) uid; + last_lock.l_len = sizeof(last_login); + + if (fcntl(last_fd, F_SETLK, &last_lock) < 0) { + D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); + pam_syslog(pamh, LOG_WARNING, + "file %s is locked/read", _PATH_LASTLOG); + sleep(LASTLOG_IGNORE_LOCK_TIME); + } - retval = conv->conv(nargs, ( const struct pam_message ** ) message - , response, conv->appdata_ptr); + if (pam_modutil_read(last_fd, (char *) &last_login, + sizeof(last_login)) != sizeof(last_login)) { + memset(&last_login, 0, sizeof(last_login)); + } - D(("returned from application's conversation function")); + last_lock.l_type = F_UNLCK; + (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ - if (retval != PAM_SUCCESS && (ctrl & LASTLOG_DEBUG) ) { - _log_err(LOG_DEBUG, "conversation failure [%s]" - , pam_strerror(pamh, retval)); + if (!last_login.ll_time) { + if (announce & LASTLOG_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "first login for user with uid %lu", + (unsigned long int)uid); } - - } else { - _log_err(LOG_ERR, "couldn't obtain coversation function [%s]" - , pam_strerror(pamh, retval)); - if (retval == PAM_SUCCESS) - retval = PAM_BAD_ITEM; /* conv was NULL */ } - D(("ready to return from module conversation")); + if (!(announce & LASTLOG_QUIET)) { - return retval; /* propagate error status */ -} + if (last_login.ll_time) { -static int make_remark(pam_handle_t *pamh, int ctrl, const char *remark) -{ - int retval; + /* we want the date? */ + if (announce & LASTLOG_DATE) { + struct tm *tm, tm_buf; + time_t ll_time; - if (!(ctrl & LASTLOG_QUIET)) { - struct pam_message msg[1], *mesg[1]; - struct pam_response *resp=NULL; + ll_time = last_login.ll_time; + tm = localtime_r (&ll_time, &tm_buf); + strftime (the_time, sizeof (the_time), + /* TRANSLATORS: "strftime options for date of last login" */ + _(" %a %b %e %H:%M:%S %Z %Y"), tm); - mesg[0] = &msg[0]; - msg[0].msg_style = PAM_TEXT_INFO; - msg[0].msg = remark; + date = the_time; + } - retval = converse(pamh, ctrl, 1, mesg, &resp); + /* we want & have the host? */ + if ((announce & LASTLOG_HOST) + && (last_login.ll_host[0] != '\0')) { + /* TRANSLATORS: " from <host>" */ + if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, + last_login.ll_host) < 0) { + pam_syslog(pamh, LOG_ERR, "out of memory"); + retval = PAM_BUF_ERR; + goto cleanup; + } + } + + /* we want and have the terminal? */ + if ((announce & LASTLOG_LINE) + && (last_login.ll_line[0] != '\0')) { + /* TRANSLATORS: " on <terminal>" */ + if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, + last_login.ll_line) < 0) { + pam_syslog(pamh, LOG_ERR, "out of memory"); + retval = PAM_BUF_ERR; + goto cleanup; + } + } - msg[0].msg = NULL; - if (resp) { - _pam_drop_reply(resp, 1); + /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */ + retval = pam_info(pamh, _("Last login:%s%s%s"), + date ? date : "", + host ? host : "", + line ? line : ""); + } else if (announce & LASTLOG_NEVER) { + D(("this is the first time this user has logged in")); + retval = pam_info(pamh, "%s", _("Welcome to your new account!")); } - } else { - D(("keeping quiet")); - retval = PAM_SUCCESS; } - D(("returning %s", pam_strerror(pamh, retval))); + /* cleanup */ + cleanup: + memset(&last_login, 0, sizeof(last_login)); + _pam_overwrite(date); + _pam_overwrite(host); + _pam_drop(host); + _pam_overwrite(line); + _pam_drop(line); + return retval; } -/* - * Values for the announce flags.. - */ - -static int last_login_date(pam_handle_t *pamh, int announce, uid_t uid) +static int +last_login_write(pam_handle_t *pamh, int announce, int last_fd, + uid_t uid, const char *user) { struct flock last_lock; struct lastlog last_login; - int retval = PAM_SESSION_ERR; - int last_fd; - - /* obtain the last login date and all the relevant info */ - last_fd = open(_PATH_LASTLOG, O_RDWR); - if (last_fd < 0) { - D(("unable to open the %s file", _PATH_LASTLOG)); - if (announce & LASTLOG_DEBUG) { - _log_err(LOG_DEBUG, "unable to open %s file", _PATH_LASTLOG); - } - retval = PAM_PERM_DENIED; - } else { - int win; + time_t ll_time; + const void *void_remote_host = NULL; + const char *remote_host; + const char *terminal_line; + int retval = PAM_SUCCESS; + + /* rewind */ + if (lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET) < 0) { + pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG); + return PAM_SERVICE_ERR; + } - /* read the lastlogin file - for this uid */ - (void) lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET); + /* set this login date */ + D(("set the most recent login time")); + (void) time(&ll_time); /* set the time */ + last_login.ll_time = ll_time; - memset(&last_lock, 0, sizeof(last_lock)); - last_lock.l_type = F_RDLCK; - last_lock.l_whence = SEEK_SET; - last_lock.l_start = sizeof(last_login) * (off_t) uid; - last_lock.l_len = sizeof(last_login); + /* set the remote host */ + if (pam_get_item(pamh, PAM_RHOST, &void_remote_host) != PAM_SUCCESS + || void_remote_host == NULL) { + remote_host = DEFAULT_HOST; + } else { + remote_host = void_remote_host; + } - if ( fcntl(last_fd, F_SETLK, &last_lock) < 0 ) { - D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); - _log_err(LOG_ALERT, "%s file is locked/read", _PATH_LASTLOG); - sleep(LASTLOG_IGNORE_LOCK_TIME); - } + /* copy to last_login */ + last_login.ll_host[0] = '\0'; + strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1); - win = (_pammodutil_read (last_fd, (char *) &last_login, - sizeof(last_login)) == sizeof(last_login)); + /* set the terminal line */ + terminal_line = get_tty(pamh); - last_lock.l_type = F_UNLCK; - (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ + /* copy to last_login */ + last_login.ll_line[0] = '\0'; + strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1); + terminal_line = NULL; - if (!win) { - D(("First login for user uid=%d", _PATH_LASTLOG, uid)); - if (announce & LASTLOG_DEBUG) { - _log_err(LOG_DEBUG, "creating lastlog for uid %d", uid); - } - memset(&last_login, 0, sizeof(last_login)); - } + D(("locking lastlog file")); - /* rewind */ - (void) lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET); + /* now we try to lock this file-record exclusively; non-blocking */ + memset(&last_lock, 0, sizeof(last_lock)); + last_lock.l_type = F_WRLCK; + last_lock.l_whence = SEEK_SET; + last_lock.l_start = sizeof(last_login) * (off_t) uid; + last_lock.l_len = sizeof(last_login); - if (!(announce & LASTLOG_QUIET)) { - if (last_login.ll_time) { - time_t ll_time; - char *the_time; - char *remark; + if (fcntl(last_fd, F_SETLK, &last_lock) < 0) { + D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); + pam_syslog(pamh, LOG_WARNING, "file %s is locked/write", _PATH_LASTLOG); + sleep(LASTLOG_IGNORE_LOCK_TIME); + } - ll_time = last_login.ll_time; - the_time = ctime(&ll_time); - the_time[-1+strlen(the_time)] = '\0'; /* delete '\n' */ + D(("writing to the lastlog file")); + if (pam_modutil_write (last_fd, (char *) &last_login, + sizeof (last_login)) != sizeof(last_login)) { + pam_syslog(pamh, LOG_ERR, "failed to write %s: %m", _PATH_LASTLOG); + retval = PAM_SERVICE_ERR; + } - remark = malloc(LASTLOG_MAXSIZE); - if (remark == NULL) { - D(("no memory for last login remark")); - retval = PAM_BUF_ERR; - } else { - int at; - - /* printing prefix */ - at = sprintf(remark, "%s", LASTLOG_INTRO); - - /* we want the date? */ - if (announce & LASTLOG_DATE) { - at += sprintf(remark+at, LASTLOG_TIME, the_time); - } - - /* we want & have the host? */ - if ((announce & LASTLOG_HOST) - && (last_login.ll_host[0] != '\0')) { - char format[2*sizeof(_LASTLOG_HOST_FORMAT)]; - - (void) sprintf(format, _LASTLOG_HOST_FORMAT - , UT_HOSTSIZE); - D(("format: %s", format)); - at += sprintf(remark+at, format, last_login.ll_host); - _pam_overwrite(format); - } - - /* we want and have the terminal? */ - if ((announce & LASTLOG_LINE) - && (last_login.ll_line[0] != '\0')) { - char format[2*sizeof(_LASTLOG_LINE_FORMAT)]; - - (void) sprintf(format, _LASTLOG_LINE_FORMAT - , UT_LINESIZE); - D(("format: %s", format)); - at += sprintf(remark+at, format, last_login.ll_line); - _pam_overwrite(format); - } - - /* display requested combo */ - sprintf(remark+at, "%s", LASTLOG_TAIL); - - retval = make_remark(pamh, announce, remark); - - /* free all the stuff malloced */ - _pam_overwrite(remark); - _pam_drop(remark); - } - } else if ((!last_login.ll_time) && (announce & LASTLOG_NEVER)) { - D(("this is the first time this user has logged in")); - retval = make_remark(pamh, announce, LASTLOG_NEVER_WELCOME); - } - } else { - D(("no text was requested")); - retval = PAM_SUCCESS; - } + last_lock.l_type = F_UNLCK; + (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ + D(("unlocked")); - /* write latest value */ - { - time_t ll_time; - const char *remote_host=NULL - , *terminal_line=DEFAULT_TERM; + if (announce & LASTLOG_WTMP) { + /* write wtmp entry for user */ + logwtmp(last_login.ll_line, user, remote_host); + } - /* set this login date */ - D(("set the most recent login time")); + /* cleanup */ + memset(&last_login, 0, sizeof(last_login)); - (void) time(&ll_time); /* set the time */ - last_login.ll_time = ll_time; + return retval; +} - /* set the remote host */ - (void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host); - if (remote_host == NULL) { - remote_host = DEFAULT_HOST; - } +static int +last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user) +{ + int retval; + int last_fd; - /* copy to last_login */ - strncpy(last_login.ll_host, remote_host, - sizeof(last_login.ll_host)); - last_login.ll_host[sizeof(last_login.ll_host) - 1] = '\0'; - remote_host = NULL; - - /* set the terminal line */ - (void) pam_get_item(pamh, PAM_TTY, (const void **)&terminal_line); - D(("terminal = %s", terminal_line)); - if (terminal_line == NULL) { - terminal_line = DEFAULT_TERM; - } else if ( !strncmp("/dev/", terminal_line, 5) ) { - /* strip leading "/dev/" from tty.. */ - terminal_line += 5; - } - D(("terminal = %s", terminal_line)); - - /* copy to last_login */ - strncpy(last_login.ll_line, terminal_line, - sizeof(last_login.ll_line)); - last_login.ll_host[sizeof(last_login.ll_host) - 1] = '\0'; - terminal_line = NULL; - - D(("locking last_log file")); - - /* now we try to lock this file-record exclusively; non-blocking */ - memset(&last_lock, 0, sizeof(last_lock)); - last_lock.l_type = F_WRLCK; - last_lock.l_whence = SEEK_SET; - last_lock.l_start = sizeof(last_login) * (off_t) uid; - last_lock.l_len = sizeof(last_login); - - if ( fcntl(last_fd, F_SETLK, &last_lock) < 0 ) { - D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); - _log_err(LOG_ALERT, "%s file is locked/write", _PATH_LASTLOG); - sleep(LASTLOG_IGNORE_LOCK_TIME); - } + /* obtain the last login date and all the relevant info */ + last_fd = open(_PATH_LASTLOG, O_RDWR); + if (last_fd < 0) { + if (errno == ENOENT) { + last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (last_fd < 0) { + pam_syslog(pamh, LOG_ERR, + "unable to create %s: %m", _PATH_LASTLOG); + D(("unable to create %s file", _PATH_LASTLOG)); + return PAM_SERVICE_ERR; + } + pam_syslog(pamh, LOG_WARNING, + "file %s created", _PATH_LASTLOG); + D(("file %s created", _PATH_LASTLOG)); + } else { + pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG); + D(("unable to open %s file", _PATH_LASTLOG)); + return PAM_SERVICE_ERR; + } + } - D(("writing to the last_log file")); - _pammodutil_write (last_fd, (char *) &last_login, - sizeof (last_login)); + if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) { + pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG); + D(("unable to lseek %s file", _PATH_LASTLOG)); + return PAM_SERVICE_ERR; + } - last_lock.l_type = F_UNLCK; - (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ - D(("unlocked")); + retval = last_login_read(pamh, announce, last_fd, uid); + if (retval != PAM_SUCCESS) + { + close(last_fd); + D(("error while reading lastlog file")); + return retval; + } - close(last_fd); /* all done */ - } - D(("all done with last login")); - } + retval = last_login_write(pamh, announce, last_fd, uid, user); - /* reset the last login structure */ - memset(&last_login, 0, sizeof(last_login)); + close(last_fd); + D(("all done with last login")); return retval; } /* --- authentication management functions (only) --- */ -PAM_EXTERN -int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc - , const char **argv) +PAM_EXTERN int +pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) { int retval, ctrl; - const char *user; + const void *user; const struct passwd *pwd; uid_t uid; @@ -413,29 +385,29 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc * last login info and then updates the lastlog for that user. */ - ctrl = _pam_parse(flags, argc, argv); + ctrl = _pam_parse(pamh, flags, argc, argv); /* which user? */ - retval = pam_get_item(pamh, PAM_USER, (const void **)&user); - if (retval != PAM_SUCCESS || user == NULL || *user == '\0') { - _log_err(LOG_NOTICE, "user unknown"); + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') { + pam_syslog(pamh, LOG_NOTICE, "user unknown"); return PAM_USER_UNKNOWN; } /* what uid? */ - pwd = _pammodutil_getpwnam (pamh, user); + pwd = pam_modutil_getpwnam (pamh, user); if (pwd == NULL) { D(("couldn't identify user %s", user)); - return PAM_CRED_INSUFFICIENT; + return PAM_USER_UNKNOWN; } uid = pwd->pw_uid; pwd = NULL; /* tidy up */ /* process the current login attempt (indicate last) */ - retval = last_login_date(pamh, ctrl, uid); + retval = last_login_date(pamh, ctrl, uid, user); /* indicate success or failure */ @@ -444,10 +416,20 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc return retval; } -PAM_EXTERN -int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc - ,const char **argv) +PAM_EXTERN int +pam_sm_close_session (pam_handle_t *pamh, int flags, + int argc, const char **argv) { + const char *terminal_line; + + if (!(_pam_parse(pamh, flags, argc, argv) & LASTLOG_WTMP)) + return PAM_SUCCESS; + + terminal_line = get_tty(pamh); + + /* Wipe out utmp logout entry */ + logwtmp(terminal_line, "", ""); + return PAM_SUCCESS; } |