From a2ddafdebe8dc4486eae214cd5a0dd576f72ffa5 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 9 Aug 2013 23:06:22 +0530 Subject: [PATCH 1/3] 1. Added pam_sqlite3.c -> Upgraded to sqlite3.h; All existing SQLite C APIs have been replaced with SQLite3 C APIs. -> Updated crypt_make_salt() with sha512; salt length = 16 bits. -> Introduced goto statements wherever possible to reduce the risk for not freeing memory, releasing locks & handles, etc during error condition. -> Re-aligned code for better readability 2. Updated Makefile.in; Introduced a new target for pam_sqlite3.c 3. Updated configure to include sqlite3.h 4. Updated install-module to install pam_sqlite3.so 5. Update .gitingnore to ignore *.swp --- .gitignore | 1 + Makefile.in | 25 +- configure | 71 ++++- install-module | 4 + pam_sqlite3.c | 757 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 843 insertions(+), 15 deletions(-) create mode 100644 pam_sqlite3.c diff --git a/.gitignore b/.gitignore index 2e4c8b5..a5d9efa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ *.o *.so +*.swp config.guess config.sub diff --git a/Makefile.in b/Makefile.in index 34c7e9a..40082a3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,25 +1,25 @@ # $Id: Makefile.in,v 1.5 2003/06/22 22:59:45 ek Exp $ -LIBSRC= pam_sqlite.c -LIBOBJ= pam_sqlite.o pam_get_pass.o pam_std_option.o pam_get_service.o -LIBLIB= pam_sqlite.so +LIBSRC= pam_sqlite.c pam_sqlite3.c +LIBOBJ= pam_sqlite.o pam_get_pass.o pam_std_option.o pam_get_service.o +LIBOBJ3= pam_sqlite3.o pam_get_pass.o pam_std_option.o pam_get_service.o DISTDIR= pam_sqlite-0.1 ROOTDIR= -LINK= @SQLITE_LIB@ -LDLIBS= ${LINK} @LIBS@ +LDLIBS= @SQLITE_LIBS@ @LIBS@ +LDLIBS3= @SQLITE3_LIBS@ @LIBS@ INCLUDE= @SQLITE_INC@ CFLAGS= @CFLAGS@ -fPIC -DPIC -Wall -D_GNU_SOURCE ${INCLUDE} -all: ${LIBLIB} +all: pam_sqlite.so pam_sqlite3.so DISTDIRS= debian DISTFILES= acconfig.h README pam_get_pass.c pam_get_service.c pam_mod_misc.h \ - pam_sqlite.c pam_std_option.c test.c debian/changelog debian/control \ - debian/copyright debian/dirs debian/rules Makefile.in configure.in \ - config.h.in install-sh config.sub config.guess install-module configure \ - CREDITS + pam_sqlite.c pam_sqlite3.c pam_std_option.c test.c debian/changelog \ + debian/control debian/copyright debian/dirs debian/rules Makefile.in \ + configure.in config.h.in install-sh config.sub config.guess install-module \ + configure CREDITS distfiles: ${DISTFILES} @@ -36,9 +36,12 @@ ${DISTDIR}.tar.gz: distfiles dist: ${DISTDIR}.tar.gz -${LIBLIB}: ${LIBOBJ} +pam_sqlite.so: ${LIBOBJ} ${CC} ${CFLAGS} ${INCLUDE} -shared -o $@ ${LIBOBJ} ${LDLIBS} +pam_sqlite3.so: ${LIBOBJ3} + ${CC} ${CFLAGS} ${INCLUDE} -shared -o $@ ${LIBOBJ3} ${LDLIBS3} + test: test.c ${CC} ${CFLAGS} -o $@ test.c ${LDLIBS} diff --git a/configure b/configure index 78a9a48..36d8fad 100755 --- a/configure +++ b/configure @@ -594,6 +594,9 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS LIBOBJS SQLITE_LIB +SQLITE_LIBS +SQLITE3_LIB +SQLITE3_LIBS SQLITE_INC EGREP GREP @@ -3471,7 +3474,7 @@ fi $as_echo "$ac_cv_lib_crypt_crypt" >&6; } if test "x$ac_cv_lib_crypt_crypt" = x""yes; then : - LIBS="-lcrypt $LIBS -lcrypt" + LIBS="-lcrypt $LIBS -lcrypto" $as_echo "#define HAVE_CRYPT 1" >>confdefs.h @@ -3763,13 +3766,73 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite_sqlite_open" >&5 $as_echo "$ac_cv_lib_sqlite_sqlite_open" >&6; } if test "x$ac_cv_lib_sqlite_sqlite_open" = x""yes; then : - LIBS="$LIBS -lsqlite" - SQLITE_LIB=-L$SQLITE_DIR/lib - + SQLITE_LIBS="-lsqlite" + SQLITE_LIB=-L$SQLITE_DIR/lib else as_fn_error "could not determine SQLite library location" "$LINENO" 5 fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SQLite3 headers" >&5 +$as_echo_n "checking for SQLite3 headers... " >&6; } +for d in /usr/local /usr ; do + test -f $d/include/sqlite3.h && { + + SQLITE_INC="-I$d/include" + SQLITE_DIR="$d" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $d/include" >&5 +$as_echo "found in $d/include" >&6; } + } +done + +old_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -L$SQLITE_DIR/lib" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_open in -lsqlite3" >&5 +$as_echo_n "checking for sqlite3_open in -lsqlite3... " >&6; } +if test "${ac_cv_lib_sqlite3_sqlite3_open+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsqlite3 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_open (); +int +main () +{ +return sqlite3_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_sqlite3_sqlite3_open=yes +else + ac_cv_lib_sqlite3_sqlite3_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_open" >&5 +$as_echo "$ac_cv_lib_sqlite3_sqlite3_open" >&6; } +if test "x$ac_cv_lib_sqlite3_sqlite3_open" = x""yes; then : + SQLITE3_LIBS="-lsqlite3" + SQLITE3_LIB=-L$SQLITE_DIR/lib + +else + as_fn_error "could not determine SQLite3 library location" "$LINENO" 5 +fi + + LDFLAGS="$old_LDFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for debug build" >&5 diff --git a/install-module b/install-module index 68c64c6..dfcb381 100755 --- a/install-module +++ b/install-module @@ -4,16 +4,20 @@ case "$1" in freebsd*) echo "FreeBSD: Installing pam_sqlite.so to /usr/lib" + echo "FreeBSD: Installing pam_sqlite3.so to /usr/lib" destdir=usr/lib ;; linux*) echo "Linux: Installing pam_sqlite.so to /lib/security" + echo "Linux: Installing pam_sqlite3.so to /lib/security" destdir=lib/security ;; *) echo "Unknown: Installing pam_sqlite.so to /lib/security" + echo "Unknown: Installing pam_sqlite3.so to /lib/security" destdir=lib/security ;; esac install -c -m 644 -o root pam_sqlite.so $ROOTDIR/$destdir +install -c -m 644 -o root pam_sqlite3.so $ROOTDIR/$destdir diff --git a/pam_sqlite3.c b/pam_sqlite3.c new file mode 100644 index 0000000..ec5b370 --- /dev/null +++ b/pam_sqlite3.c @@ -0,0 +1,757 @@ +/* + * PAM authentication module for SQLite + * + * SQLite port: Edin Kadribasic + * Extended SQL configuration support by Wez Furlong + * + * Based in part on pam_pgsql.c by David D.W. Downey ("pgpkeys") + * + * Based in part on pam_unix.c of FreeBSD. + * + */ + +/* $Id: pam_sqlite.c,v 1.11 2003/07/17 13:47:07 wez Exp $ */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#if HAVE_CRYPT_H +#include +#endif +#include + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_PASSWORD +#include +#include "pam_mod_misc.h" + +#define PASSWORD_PROMPT "Password: " +#define PASSWORD_PROMPT_NEW "New password: " +#define PASSWORD_PROMPT_CONFIRM "Confirm new password: " +#define CONF "/etc/pam_sqlite.conf" + +#define DBGLOG(x...) printf(x); \ + if(options->debug) { \ + openlog("PAM_sqlite", LOG_PID, LOG_AUTH); \ + syslog(LOG_DEBUG, ##x); \ + closelog(); \ + } +#define SYSLOG(x...) do { \ + openlog("PAM_sqlite", LOG_PID, LOG_AUTH); \ + syslog(LOG_INFO, ##x); \ + closelog(); \ + printf(x); \ + } while(0); + +typedef enum { + PW_CLEAR = 1, +#if HAVE_MD5_CRYPT + PW_MD5, +#endif + PW_CRYPT, +} pw_scheme; + +struct module_options { + char *database; + char *table; + char *user_column; + char *pwd_column; + char *expired_column; + char *newtok_column; + pw_scheme pw_type; + int debug; + char *sql_verify; + char *sql_check_expired; + char *sql_check_newtok; + char *sql_set_passwd; +}; + +#define GROW(x) if (x > buflen - dest - 1) { \ + char *grow; \ + buflen += 256 + x; \ + grow = realloc(buf, buflen + 256 + x); \ + if (grow == NULL) { free(buf); return NULL; } \ + buf = grow; \ +} + +#define APPEND(str, len) GROW(len); memcpy(buf + dest, str, len); dest += len +#define APPENDS(str) len = strlen(str); APPEND(str, len) + +static char * format_query(const char *template, + struct module_options *options, + const char *user, + const char *passwd) +{ + char *buf = malloc(256); + int buflen = 256; + int dest = 0, len; + const char *src = template; + char *pct; + char *tmp; + + while (*src) { + pct = strchr(src, '%'); + + if (pct) { + /* copy from current position to % char into buffer */ + if (pct != src) { + len = pct - src; + APPEND(src, len); + } + + /* decode the escape */ + switch(pct[1]) { + case 'U': /* username */ + if (user) { + tmp = sqlite3_mprintf("%q", user); + len = strlen(tmp); + APPEND(tmp, len); + sqlite3_free(tmp); + } + break; + case 'P': /* password */ + if (passwd) { + tmp = sqlite3_mprintf("%q", passwd); + len = strlen(tmp); + APPEND(tmp, len); + sqlite3_free(tmp); + } + break; + case 'O': /* option value */ + pct++; + switch (pct[1]) { + case 'p': /* passwd */ + APPENDS(options->pwd_column); + break; + case 'u': /* username */ + APPENDS(options->user_column); + break; + case 't': /* table */ + APPENDS(options->table); + break; + case 'x': /* expired */ + APPENDS(options->expired_column); + break; + case 'n': /* newtok */ + APPENDS(options->newtok_column); + break; + } + break; + case '%': /* quoted % sign */ + APPEND(pct, 1); + break; + default: /* unknown */ + APPEND(pct, 2); + break; + } + src = pct + 2; + } else { + /* copy rest of string into buffer and we're done */ + len = strlen(src); + APPEND(src, len); + break; + } + } + + buf[dest] = '\0'; + return buf; +} + +static void get_module_options_from_file(const char *filename, + struct module_options *opts, + int warn); + +/* private: parse and set the specified string option */ +static void set_module_option(const char *option, + struct module_options *options) +{ + char *buf, *eq; + char *val, *end; + + if(!option || !*option) + return; + + buf = strdup(option); + + if((eq = strchr(buf, '='))) { + end = eq - 1; + val = eq + 1; + if(end <= buf || !*val) + return; + while(end > buf && isspace(*end)) + end--; + end++; + *end = '\0'; + while(*val && isspace(*val)) + val++; + } else { + val = NULL; + } + + DBGLOG("setting option: %s=>%s\n", buf, val); + + if(!strcmp(buf, "database")) { + options->database = strdup(val); + } else if(!strcmp(buf, "table")) { + options->table = strdup(val); + } else if(!strcmp(buf, "user_column")) { + options->user_column = strdup(val); + } else if(!strcmp(buf, "pwd_column")) { + options->pwd_column = strdup(val); + } else if(!strcmp(buf, "expired_column")) { + options->expired_column = strdup(val); + } else if(!strcmp(buf, "newtok_column")) { + options->newtok_column = strdup(val); + } else if(!strcmp(buf, "pw_type")) { + options->pw_type = PW_CLEAR; + if(!strcmp(val, "crypt")) { + options->pw_type = PW_CRYPT; + } +#if HAVE_MD5_CRYPT + else if(!strcmp(val, "md5")) { + options->pw_type = PW_MD5; + } +#endif + } else if(!strcmp(buf, "debug")) { + options->debug = 1; + } else if (!strcmp(buf, "config_file")) { + get_module_options_from_file(val, options, 1); + } else if (!strcmp(buf, "sql_verify")) { + options->sql_verify = strdup(val); + } else if (!strcmp(buf, "sql_check_expired")) { + options->sql_check_expired = strdup(val); + } else if (!strcmp(buf, "sql_check_newtok")) { + options->sql_check_newtok = strdup(val); + } else if (!strcmp(buf, "sql_set_passwd")) { + options->sql_set_passwd = strdup(val); + } + + free(buf); +} + +/* private: read module options from a config file */ +static void get_module_options_from_file(const char *filename, + struct module_options *opts, + int warn) +{ + FILE *fp; + + if ((fp = fopen(filename, "r"))) { + char line[1024]; + char *str, *end; + + while(fgets(line, sizeof(line), fp)) { + str = line; + end = line + strlen(line) - 1; + while(*str && isspace(*str)) + str++; + while(end > str && isspace(*end)) + end--; + end++; + *end = '\0'; + set_module_option(str, opts); + } + fclose(fp); + } else if (warn) { + SYSLOG("unable to read config file %s", filename); + } +} + +/* private: read module options from file or commandline */ +static int get_module_options(int argc, + const char **argv, + struct module_options **options) +{ + int i, retval = 0; + struct module_options *opts; + + opts = (struct module_options *)malloc(sizeof *opts); + bzero(opts, sizeof(*opts)); + opts->pw_type = PW_CLEAR; + + get_module_options_from_file(CONF, opts, 0); + + for(i = 0; i < argc; i++) { + if(pam_std_option(&retval, argv[i]) == 0) + continue; + set_module_option(argv[i], opts); + } + *options = opts; + + return retval; +} + +/* private: free module options returned by get_module_options() */ +static void free_module_options(struct module_options *options) +{ + if(options->database) + free(options->database); + if(options->table) + free(options->table); + if(options->user_column) + free(options->user_column); + if(options->pwd_column) + free(options->pwd_column); + if(options->expired_column) + free(options->expired_column); + if(options->newtok_column) + free(options->newtok_column); + if(options->sql_verify) + free(options->sql_verify); + if(options->sql_check_expired) + free(options->sql_check_expired); + if(options->sql_check_newtok) + free(options->sql_check_newtok); + if(options->sql_set_passwd) + free(options->sql_set_passwd); + bzero(options, sizeof(*options)); + free(options); +} + +/* private: make sure required options are present (in cmdline or conf file) */ +static int options_valid(struct module_options *options) +{ + if(options->database == 0 || options->table == 0 || options->user_column == 0) + { + SYSLOG("the database, table and user_column options are required."); + return -1; + } + return 0; +} + +/* private: open SQLite database */ +static sqlite3 * pam_sqlite_connect(struct module_options *options, + int flags) +{ + sqlite3 *sdb = NULL; + int res; + + res = sqlite3_open_v2(options->database, &sdb, flags, NULL); + if (res != SQLITE_OK) { + SYSLOG("Error opening SQLite database [%s] [%s]\n", + options->database, sqlite3_errmsg(sdb)); + return NULL; + } + + DBGLOG("Successfully opened SQLite3 database [%s]\n", + options->database); + return sdb; +} + +/* private: generate random salt character */ +static char * crypt_make_salt(struct module_options *options) +{ + int i; + time_t now; + static unsigned long x; + static char result[21]; + static char salt_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; + + time(&now); + x += now + getpid() + clock(); + srandom(x); + + switch(options->pw_type) { + case PW_CRYPT: + result[0] = salt_chars[random() % 64]; + result[1] = salt_chars[random() % 64]; + result[2] = '\0'; + break; +#if HAVE_MD5_CRYPT + case PW_MD5: + result[0]='$'; + result[1]='6'; + result[2]='$'; + for (i=3; i<19; i++) { + result[i] = salt_chars[random() % 64]; + } + result[19] = '$'; + result[20]='\0'; + break; +#endif + default: + result[0] = '\0'; + } + + printf("salt = %s \n", result); + return result; +} + +/* private: encrypt password using the preferred encryption scheme */ +static char * encrypt_password(struct module_options *options, + const char *pass) +{ + char *s = NULL; + + switch(options->pw_type) { +#if HAVE_MD5_CRYPT + case PW_MD5: +#endif + case PW_CRYPT: + s = strdup(crypt(pass, crypt_make_salt(options))); + break; + case PW_CLEAR: + default: + s = strdup(pass); + } + + return s; +} + +/* private: authenticate username and password against database */ +static int auth_verify_password(const char *un, + const char *pwd, + struct module_options *options) +{ + sqlite3 *sdb; + sqlite3_stmt *stmt; + int retval, result; + char *query; + + if(!(sdb = pam_sqlite_connect(options, SQLITE_OPEN_READONLY))) + return PAM_AUTH_ERR; + + query = format_query(options->sql_verify ? options->sql_verify : + "SELECT %Op FROM %Ot WHERE %Ou='%U'", + options, un, pwd); + DBGLOG("query: %s \n", query); + + /* prepare the sql statement to be executed */ + retval = sqlite3_prepare_v2(sdb, query, -1, &stmt, NULL); + + if (retval != SQLITE_OK) { + DBGLOG("Error executing SQLite query [%s]", + sqlite3_errmsg(sdb)); + result = PAM_AUTH_ERR; + goto cleanup; + } + + /* execute the sql statement and get the first row */ + retval = sqlite3_step(stmt); + + /* if the select query results in no rows, then the user is not available */ + if (retval == SQLITE_DONE) { + DBGLOG("No record found for user [%s] [%s]", + un, sqlite3_errmsg(sdb)); + result = PAM_USER_UNKNOWN; + goto cleanup; + } + + /* if the select query does not yield a valid row, then fail */ + if (retval != SQLITE_ROW) { + DBGLOG("Unable to reterieve user record [%s] [%s]", + un, sqlite3_errmsg(sdb)); + result = PAM_AUTH_ERR; + goto cleanup; + } + + /* get the encrypted password from the database */ + const char *stored_pwd = (char *)sqlite3_column_text(stmt, 0); + + result = PAM_AUTH_ERR; + switch(options->pw_type) { + case PW_CLEAR: + if(strcmp(pwd, stored_pwd) == 0) + result = PAM_SUCCESS; + break; +#if HAVE_MD5_CRYPT + case PW_MD5: +#endif + case PW_CRYPT: + if(strcmp(crypt(pwd, stored_pwd), stored_pwd) == 0) + result = PAM_SUCCESS; + break; + } + +cleanup: + free(query); + sqlite3_finalize(stmt); + sqlite3_close(sdb); + return result; +} + +/* public: authenticate user */ +PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, + int flags, + int argc, + const char **argv) +{ + struct module_options *options; + const char *un, *pwd; + int std_flags; + int result; + + if ((result = pam_get_user(pamh, &un, NULL)) != PAM_SUCCESS) + return result; + + std_flags = get_module_options(argc, argv, &options); + if (options_valid(options) != 0) { + result = PAM_AUTH_ERR; + goto cleanup; + } + + if ((result = pam_get_pass(pamh, &pwd, PASSWORD_PROMPT, std_flags) + != PAM_SUCCESS)) + goto cleanup; + + if ((result = auth_verify_password(un, pwd, options)) == PAM_SUCCESS) + SYSLOG("[%s] user %s authenticated.\n", pam_get_service(pamh), un); + +cleanup: + free_module_options(options); + return result; +} + +/* public: check if account has expired, or needs new password */ +PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, + int flags, + int argc, + const char **argv) +{ + struct module_options *options; + const char *un; + sqlite3 *sdb; + sqlite3_stmt *stmt; + char *query = NULL; + int retval, result = PAM_SUCCESS; + + get_module_options(argc, argv, &options); + if (options_valid(options) != 0) { + result = PAM_AUTH_ERR; + goto cleanup_1; + } + + /* both not specified, just succeed. */ + if (options->expired_column == 0 && options->newtok_column == 0) { + result = PAM_SUCCESS; + goto cleanup_1; + } + + if ((retval = pam_get_user(pamh, &un, NULL)) != PAM_SUCCESS) { + DBGLOG("Unable to retervie username\n"); + result = retval; + goto cleanup_1; + } + + if (!(sdb = pam_sqlite_connect(options, SQLITE_OPEN_READONLY))) { + result = PAM_AUTH_ERR; + goto cleanup_1; + } + + /* if account has expired then expired_column = '1' or 'y' */ + if(options->expired_column || options->sql_check_expired) { + query = format_query(options->sql_check_expired ? options->sql_check_expired : + "SELECT 1 from %Ot WHERE %Ou='%U' AND (%Ox='y' OR %Ox='1')", + options, un, NULL); + DBGLOG("query: %s", query); + + retval = sqlite3_prepare_v2(sdb, query, -1, &stmt, NULL); + if (retval != SQLITE_OK) { + DBGLOG("Error executing SQLite query [%s]", sqlite3_errmsg(sdb)); + result = PAM_AUTH_ERR; + goto cleanup_2; + } + + retval = sqlite3_step(stmt); + if (retval == SQLITE_ROW) { + result = PAM_ACCT_EXPIRED; + goto cleanup_2; + } + + if (retval != SQLITE_DONE) { + result = PAM_AUTH_ERR; + goto cleanup_2; + } + } + + /* if new password is required then newtok_column = 'y' or '1' */ + if(options->newtok_column || options->sql_check_newtok) { + query = format_query(options->sql_check_newtok ? options->sql_check_newtok : + "SELECT 1 FROM %Ot WHERE %Ou='%U' AND (%On='y' OR %On='1')", + options, un, NULL); + DBGLOG("query: %s", query); + + retval = sqlite3_prepare_v2(sdb, query, -1, &stmt, NULL); + if (retval != SQLITE_OK) { + DBGLOG("Error executing SQLite query [%s]", sqlite3_errmsg(sdb)); + result = PAM_AUTH_ERR; + goto cleanup_2; + } + + retval = sqlite3_step(stmt); + if (retval == SQLITE_ROW) { + result = PAM_NEW_AUTHTOK_REQD; + goto cleanup_2; + } + + if (retval != SQLITE_DONE) { + result = PAM_AUTH_ERR; + goto cleanup_2; + } + } + + result = PAM_SUCCESS; + +cleanup_2: + free(query); + sqlite3_finalize(stmt); + sqlite3_close(sdb); + +cleanup_1: + free_module_options(options); + return result; +} + +/* public: change password */ +PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, + int flags, + int argc, + const char **argv) +{ + struct module_options *options; + const char *un, *pwd, *new_pwd; + char *new_pwd_crypt = NULL; + sqlite3 *sdb = NULL; + char *errtext = NULL, *query = NULL; + int std_flags, retval, result; + + std_flags = get_module_options(argc, argv, &options); + if (options_valid(options) != 0) { + result = PAM_AUTH_ERR; + goto cleanup_1; + } + + if ((retval = pam_get_user(pamh, &un, NULL)) != PAM_SUCCESS) { + result = retval; + goto cleanup_1; + } + + if (flags & PAM_PRELIM_CHECK) { + /* at this point, this is the first time we get called */ + if ((retval = pam_get_pass(pamh, &pwd, PASSWORD_PROMPT, std_flags)) != PAM_SUCCESS) { + SYSLOG("could not retrieve password from '%s'", un); + result = PAM_AUTH_ERR; + goto cleanup_1; + } + + if ((retval = auth_verify_password(un, pwd, options)) != PAM_SUCCESS) { + DBGLOG("password verification failed for '%s'", un); + result = retval; + goto cleanup_1; + } + + retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)pwd); + if (retval != PAM_SUCCESS) + SYSLOG("failed to set PAM_OLDAUTHTOK!"); + result = retval; + goto cleanup_1; + + } else if (flags & PAM_UPDATE_AUTHTOK) { + + pwd = new_pwd = NULL; + + retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **) &pwd); + if (retval != PAM_SUCCESS) { + SYSLOG("could not retrieve old token"); + result = retval; + goto cleanup_1; + } + + retval = auth_verify_password(un, pwd, options); + if (retval != PAM_SUCCESS) { + SYSLOG("(%s) user '%s' not authenticated.", pam_get_service(pamh), un); + result = retval; + goto cleanup_1; + } + + /* get and confirm the new passwords */ + retval = pam_get_confirm_pass(pamh, &new_pwd, PASSWORD_PROMPT_NEW, + PASSWORD_PROMPT_CONFIRM, std_flags); + if (retval != PAM_SUCCESS) { + SYSLOG("could not retrieve new authentication tokens"); + result = retval; + goto cleanup_1; + } + + /* save the new password for subsequently stacked modules */ + retval = pam_set_item(pamh, PAM_AUTHTOK, (const void *)new_pwd); + if (retval != PAM_SUCCESS) { + SYSLOG("failed to set PAM_AUTHTOK!"); + result = retval; + goto cleanup_1; + } + + /* update the database */ + if (!(new_pwd_crypt = encrypt_password(options, new_pwd))) { + DBGLOG("passwd encrypt failed"); + result = PAM_BUF_ERR; + goto cleanup_1; + } + + if(!(sdb = pam_sqlite_connect(options, SQLITE_OPEN_READWRITE))) { + result = PAM_AUTHINFO_UNAVAIL; + goto cleanup_2; + } + + query = format_query(options->sql_set_passwd ? options->sql_set_passwd : + "UPDATE %Ot SET %Op='%P' WHERE %Ou='%U'", + options, un, new_pwd_crypt); + DBGLOG("query: %s", query); + + retval = sqlite3_exec(sdb, query, NULL, NULL, &errtext); + + if (retval != SQLITE_OK) { + DBGLOG("query failed[%d]: %s", retval, errtext); + result = PAM_AUTH_ERR; + goto cleanup_3; + } + /* if we get here, we must have succeeded */ + } + + result = PAM_SUCCESS; + SYSLOG("[%s] password for '%s' was changed.\n", + pam_get_service(pamh), un); + +cleanup_3: + free(query); + sqlite3_free(errtext); + sqlite3_close(sdb); + +cleanup_2: + free(new_pwd_crypt); + +cleanup_1: + free_module_options(options); + return result; +} + +/* public: just succeed. */ +PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, + int flags, + int argc, + const char **argv) +{ + return PAM_SUCCESS; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ From 68e5e2d47d8f59b73a64ecbf9df41d6b93748f67 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 9 Aug 2013 23:35:30 +0530 Subject: [PATCH 2/3] Updated SYSLOGs for error cases. --- pam_sqlite3.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pam_sqlite3.c b/pam_sqlite3.c index ec5b370..565ce30 100644 --- a/pam_sqlite3.c +++ b/pam_sqlite3.c @@ -30,7 +30,6 @@ #if HAVE_CRYPT_H #include #endif -#include #define PAM_SM_AUTH #define PAM_SM_ACCOUNT @@ -43,8 +42,7 @@ #define PASSWORD_PROMPT_CONFIRM "Confirm new password: " #define CONF "/etc/pam_sqlite.conf" -#define DBGLOG(x...) printf(x); \ - if(options->debug) { \ +#define DBGLOG(x...) if(options->debug) { \ openlog("PAM_sqlite", LOG_PID, LOG_AUTH); \ syslog(LOG_DEBUG, ##x); \ closelog(); \ @@ -53,7 +51,6 @@ openlog("PAM_sqlite", LOG_PID, LOG_AUTH); \ syslog(LOG_INFO, ##x); \ closelog(); \ - printf(x); \ } while(0); typedef enum { @@ -386,7 +383,6 @@ static char * crypt_make_salt(struct module_options *options) result[0] = '\0'; } - printf("salt = %s \n", result); return result; } @@ -507,8 +503,12 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, != PAM_SUCCESS)) goto cleanup; - if ((result = auth_verify_password(un, pwd, options)) == PAM_SUCCESS) + if ((result = auth_verify_password(un, pwd, options)) == PAM_SUCCESS) { SYSLOG("[%s] user %s authenticated.\n", pam_get_service(pamh), un); + } else { + SYSLOG("[%s] unable to authenticate user %s [%s]\n", + pam_get_service(pamh), un, pam_strerror(NULL, result)); + } cleanup: free_module_options(options); @@ -567,6 +567,7 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, retval = sqlite3_step(stmt); if (retval == SQLITE_ROW) { + SYSLOG("[%s] user %s account expired.\n", pam_get_service(pamh), un); result = PAM_ACCT_EXPIRED; goto cleanup_2; } @@ -593,6 +594,8 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, retval = sqlite3_step(stmt); if (retval == SQLITE_ROW) { + SYSLOG("[%s] user %s account requires new authentication token.\n", + pam_get_service(pamh), un); result = PAM_NEW_AUTHTOK_REQD; goto cleanup_2; } @@ -672,7 +675,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, retval = auth_verify_password(un, pwd, options); if (retval != PAM_SUCCESS) { - SYSLOG("(%s) user '%s' not authenticated.", pam_get_service(pamh), un); + SYSLOG("[%s] user '%s' not authenticated.", pam_get_service(pamh), un); result = retval; goto cleanup_1; } From 4da250a0752893baef56d0cc58d5bf7b4839da47 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 9 Aug 2013 23:57:59 +0530 Subject: [PATCH 3/3] Updated README; Removed crypto lib during compilation. --- README | 114 +-------------------------------------------- README_pam_sqlite | 113 ++++++++++++++++++++++++++++++++++++++++++++ README_pam_sqlite3 | 83 +++++++++++++++++++++++++++++++++ configure | 2 +- 4 files changed, 198 insertions(+), 114 deletions(-) mode change 100644 => 120000 README create mode 100644 README_pam_sqlite create mode 100644 README_pam_sqlite3 diff --git a/README b/README deleted file mode 100644 index 314a268..0000000 --- a/README +++ /dev/null @@ -1,113 +0,0 @@ -pam_sqlite 0.3 -============= - -Introduction -============ - -This module provides support to authenticate against SQLite -tables for PAM-enabled appliations. - -This module is based on pam_pgsql module. - -Compilation & Installation -========================== - -pam_sqlite is now autoconf'ed, thus, compiling should be a matter -of: - - $ ./configure - $ make - $ make install - -Compilation has been tested on RedHat Linux 7.3. - -You will need to have SQLite library and header files -for this module to compile. - -See test.c for an example application that authenticates using -this module. - -Configuration -============= - -For the service you wish the module to be used, you need -to edit the /etc/pam.d/ file or /etc/pam.conf, and -add the relevant lines. - -For example: - -auth required pam_sqlite.so -account required pam_sqlite.so -password required pam_sqlite.so - -Configure the database, and table the module should use with -the configuration file /etc/pam_sqlite.conf. An example of -this file: - -database = /etc/sysdb -table = account -user_column = user_name -pwd_column = user_password -expired_column = acc_expired -newtok_column = acc_new_pwreq -debug - -expired_column tells PAM if the user account has expired. Set it to '1' -or 'y' if it has. - -newtok_column tells PAM if the user needs a new password. Set it to '1' -or 'y' if it does. - -Note that for backwards compatibility with earlier versions, options specified -in the configuration file can be supplied as module arguments as well. Module -arguments will override the configuration file. - -Configuration Options -===================== - - database - the database which should be connected to - table - the name of the table to query - user_column - the column containing usernames - pwd_column - the column containing the passwords - expired_column - this column should contain '1' or 'y' if the account - has expired - newtok_column - this column should contain '1' or 'y' if the user - needs to change their password - debug - this is a standard module option that will enable - debug output to syslog (takes no values) - pw_type - specifies the password encryption scheme, can be one - of 'clear', 'md5', or 'crypt'. defaults to 'clear'. - config_file - specifies the path to a file to read for further - configuration options - sql_verify - specifies SQL template to use when verifying the - the password for a user - sql_check_expired - SQL template to use when checking for account expiry. - sql_check_newtok - SQL template to use when checking to see if the user - needs to change their password. - sql_set_passwd - SQL template to use when updating the password for - and user. - - -SQL Templates -============= - -SQL templates are printf-inspired format strings. The following escape -sequences are understood: - - %% - literal % character - - %U - The username (provided by PAM). It will be quoted for use - in the SQL. - %P - The password, either entered by the user or the new password - to use when changing it. It will be quoted for use in SQL. - - %O - an option from the configuration; the following options are - supported: - - %Op - value of pwd_column - %Ou - value of user_column - %Ot - value of table - %Ox - value of expired_column - %On - value of newtok_column - -vim:et: diff --git a/README b/README new file mode 120000 index 0000000..2b97dd7 --- /dev/null +++ b/README @@ -0,0 +1 @@ +README_pam_sqlite3 \ No newline at end of file diff --git a/README_pam_sqlite b/README_pam_sqlite new file mode 100644 index 0000000..314a268 --- /dev/null +++ b/README_pam_sqlite @@ -0,0 +1,113 @@ +pam_sqlite 0.3 +============= + +Introduction +============ + +This module provides support to authenticate against SQLite +tables for PAM-enabled appliations. + +This module is based on pam_pgsql module. + +Compilation & Installation +========================== + +pam_sqlite is now autoconf'ed, thus, compiling should be a matter +of: + + $ ./configure + $ make + $ make install + +Compilation has been tested on RedHat Linux 7.3. + +You will need to have SQLite library and header files +for this module to compile. + +See test.c for an example application that authenticates using +this module. + +Configuration +============= + +For the service you wish the module to be used, you need +to edit the /etc/pam.d/ file or /etc/pam.conf, and +add the relevant lines. + +For example: + +auth required pam_sqlite.so +account required pam_sqlite.so +password required pam_sqlite.so + +Configure the database, and table the module should use with +the configuration file /etc/pam_sqlite.conf. An example of +this file: + +database = /etc/sysdb +table = account +user_column = user_name +pwd_column = user_password +expired_column = acc_expired +newtok_column = acc_new_pwreq +debug + +expired_column tells PAM if the user account has expired. Set it to '1' +or 'y' if it has. + +newtok_column tells PAM if the user needs a new password. Set it to '1' +or 'y' if it does. + +Note that for backwards compatibility with earlier versions, options specified +in the configuration file can be supplied as module arguments as well. Module +arguments will override the configuration file. + +Configuration Options +===================== + + database - the database which should be connected to + table - the name of the table to query + user_column - the column containing usernames + pwd_column - the column containing the passwords + expired_column - this column should contain '1' or 'y' if the account + has expired + newtok_column - this column should contain '1' or 'y' if the user + needs to change their password + debug - this is a standard module option that will enable + debug output to syslog (takes no values) + pw_type - specifies the password encryption scheme, can be one + of 'clear', 'md5', or 'crypt'. defaults to 'clear'. + config_file - specifies the path to a file to read for further + configuration options + sql_verify - specifies SQL template to use when verifying the + the password for a user + sql_check_expired - SQL template to use when checking for account expiry. + sql_check_newtok - SQL template to use when checking to see if the user + needs to change their password. + sql_set_passwd - SQL template to use when updating the password for + and user. + + +SQL Templates +============= + +SQL templates are printf-inspired format strings. The following escape +sequences are understood: + + %% - literal % character + + %U - The username (provided by PAM). It will be quoted for use + in the SQL. + %P - The password, either entered by the user or the new password + to use when changing it. It will be quoted for use in SQL. + + %O - an option from the configuration; the following options are + supported: + + %Op - value of pwd_column + %Ou - value of user_column + %Ot - value of table + %Ox - value of expired_column + %On - value of newtok_column + +vim:et: diff --git a/README_pam_sqlite3 b/README_pam_sqlite3 new file mode 100644 index 0000000..34d6190 --- /dev/null +++ b/README_pam_sqlite3 @@ -0,0 +1,83 @@ +PAM module for SQLite3 +====================== + + +Introduction +============ + +This is an enhancement done on the original project in order to +provide PAM support for SQLite3. + - https://github.com/Sectoid/libpam-sqlite + - git@github.com:Sectoid/libpam-sqlite.git + + +Download +======== + + $ git clone git@github.com:sangeeths/libpam-sqlite.git + + +Compilation & Installation +========================== + +NOTE: If you come across issues on libtool, then add the + following softlinks from the libpam-sqlite directory. + In my machine I had the following softlinks. + +config.guess -> /usr/share/libtool/config/config.guess +config.sub -> /usr/share/libtool/config/config.sub + + $ ./configure + $ make + $ make install + +Compilation has been tested on + -> Fedora Core 18 running 3.6.10-4 on x86_64 + +You will need to have SQLite and SQLite3 library and header +files for this module to compile. + + +Configuration +============= + +For the service you wish the module to be used, you need +to edit the /etc/pam.d/ file or /etc/pam.conf, and +add the relevant lines. + +For example: + +auth required /lib/security/pam_sqlite3.so +account required /lib/security/pam_sqlite3.so +password required /lib/security/pam_sqlite3.so + +Configure the database, and table the module should use with +the configuration file /etc/pam_sqlite.conf. An example of +this file: + +database = /etc/sysdb +table = account +user_column = user_name +pwd_column = user_password +expired_column = acc_expired +newtok_column = acc_new_pwreq +debug + +/etc/sysdb should be a SQLite3 database file. + +expired_column tells PAM if the user account has expired. Set it to '1' +or 'y' if it has. + +newtok_column tells PAM if the user needs a new password. Set it to '1' +or 'y' if it does. + + +References +========== +Please go through the README of the original project for +detailed explanation on various configurable options, +format specifiers, etc. + - https://github.com/Sectoid/libpam-sqlite/blob/master/README + + +__END__ diff --git a/configure b/configure index 36d8fad..5c916b7 100755 --- a/configure +++ b/configure @@ -3474,7 +3474,7 @@ fi $as_echo "$ac_cv_lib_crypt_crypt" >&6; } if test "x$ac_cv_lib_crypt_crypt" = x""yes; then : - LIBS="-lcrypt $LIBS -lcrypto" + LIBS="-lcrypt $LIBS" $as_echo "#define HAVE_CRYPT 1" >>confdefs.h