diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-09-20 20:01:11 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-09-20 20:01:11 +0000 |
commit | 32b6f4c4aeddfdda9343d59fba02ae7fe3e0b24c (patch) | |
tree | ede5064d9754d063fecece08d81f8ef1e98cb3ba /src | |
parent | 125b13c1760df7ad557d0d5462b39c7f092e2f3b (diff) | |
download | smartcardauth-32b6f4c4aeddfdda9343d59fba02ae7fe3e0b24c.tar.gz smartcardauth-32b6f4c4aeddfdda9343d59fba02ae7fe3e0b24c.zip |
Use new smartauthmon C++ program instead of the old bash script for smartcard authentication
This plugs several possible security holes
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/smartcardauth@1254687 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 13 | ||||
-rw-r--r-- | src/ckpass.c | 284 | ||||
-rw-r--r-- | src/ckpasswd.c | 3 | ||||
-rw-r--r-- | src/smartauthmon.cpp | 81 |
4 files changed, 349 insertions, 32 deletions
diff --git a/src/Makefile b/src/Makefile index e5f9a26..0539c5c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,7 @@ -all: ckpasswd.o xmalloc.o messages.o ckpasswd +all: ckpasswd.o xmalloc.o messages.o ckpass.o ckpasswd smartauthmon + +ckpass.o: ckpass.c + gcc ckpass.c -c ckpasswd.o: ckpasswd.c gcc ckpasswd.c -c @@ -9,14 +12,14 @@ xmalloc.o: xmalloc.c messages.o: messages.c gcc messages.c -c -#smartauthmon.o: smartauthmon.cpp -# g++ -I/usr/include/tqt -I/usr/include/qt3 smartauthmon.cpp -c +smartauthmon.o: smartauthmon.cpp + g++ -I/usr/include/tqt -I/usr/include/qt3 smartauthmon.cpp -c ckpasswd: ckpasswd.o gcc ckpasswd.o xmalloc.o messages.o -o ckpasswd -lpam -lcrypt -#ckpasswd: smartauthmon.o -# gcc smartauthmon.o -o smartauthmon -ltqt +smartauthmon: smartauthmon.o ckpass.o + gcc smartauthmon.o ckpass.o xmalloc.o messages.o -o smartauthmon -ltqt -lpam -lcrypt clean: rm -f ckpasswd.o xmalloc.o messages.o ckpasswd diff --git a/src/ckpass.c b/src/ckpass.c new file mode 100644 index 0000000..1da83c6 --- /dev/null +++ b/src/ckpass.c @@ -0,0 +1,284 @@ +/* $Id: ckpasswd.c 7565 2006-08-28 02:42:54Z eagle $ +** +** The default username/password authenticator. +** +** This program is intended to be run by nnrpd and handle usernames and +** passwords. It can authenticate against a regular flat file (the type +** managed by htpasswd), a DBM file, the system password file or shadow file, +** or PAM. +*/ + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* Make available the bool type. */ +#if INN_HAVE_STDBOOL_H +# include <stdbool.h> +#else +# undef true +# undef false +# define true (1) +# define false (0) +# ifndef __cplusplus +# define bool int +# endif +#endif /* INN_HAVE_STDBOOL_H */ + +#include <stdlib.h> +#include <string.h> +#include <crypt.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> + +#define DB_DBM_HSEARCH 1 +#include <db.h> +#define OPT_DBM "d:" + +#if HAVE_GETSPNAM +# include <shadow.h> +# define OPT_SHADOW "s" +#else +# define OPT_SHADOW "" +#endif + +/* The functions are actually macros so that we can pick up the file and line + number information for debugging error messages without the user having to + pass those in every time. */ +#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__) +#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) +#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__) +#define xstrdup(p) x_strdup((p), __FILE__, __LINE__) +#define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__) + +#include <security/pam_appl.h> + +/* Holds the authentication information from nnrpd. */ +struct auth_info { + char *username; + char *password; +}; + +/* +** The PAM conversation function. +** +** Since we already have all the information and can't ask the user +** questions, we can't quite follow the real PAM protocol. Instead, we just +** return the password in response to every question that PAM asks. There +** appears to be no generic way to determine whether the message in question +** is indeed asking for the password.... +** +** This function allocates an array of struct pam_response to return to the +** PAM libraries that's never freed. For this program, this isn't much of an +** issue, since it will likely only be called once and then the program will +** exit. This function uses malloc and strdup instead of xmalloc and xstrdup +** intentionally so that the PAM conversation will be closed cleanly if we +** run out of memory rather than simply terminated. +** +** appdata_ptr contains the password we were given. +*/ +static int pass_conv(int num_msg, const struct pam_message **msgm UNUSED, struct pam_response **response, void *appdata_ptr) +{ + int i; + + *response = malloc(num_msg * sizeof(struct pam_response)); + if (*response == NULL) + return PAM_CONV_ERR; + for (i = 0; i < num_msg; i++) { + (*response)[i].resp = strdup((char *)appdata_ptr); + (*response)[i].resp_retcode = 0; + } + return PAM_SUCCESS; +} + + +/* +** Authenticate a user via PAM. +** +** Attempts to authenticate a user with PAM, returning true if the user +** successfully authenticates and false otherwise. Note that this function +** doesn't attempt to handle any remapping of the authenticated user by the +** PAM stack, but just assumes that the authenticated user was the same as +** the username given. +** +** Right now, all failures are handled via die. This may be worth revisiting +** in case we want to try other authentication methods if this fails for a +** reason other than the system not having PAM support. +*/ + +static bool auth_pam(const char *username, char *password) +{ + pam_handle_t *pamh; + struct pam_conv conv; + int status; + + conv.conv = pass_conv; + conv.appdata_ptr = password; + status = pam_start("nnrpd", username, &conv, &pamh); + if (status != PAM_SUCCESS) + die("pam_start failed: %s", pam_strerror(pamh, status)); + status = pam_authenticate(pamh, PAM_SILENT); + if (status != PAM_SUCCESS) + die("pam_authenticate failed: %s", pam_strerror(pamh, status)); + status = pam_acct_mgmt(pamh, PAM_SILENT); + if (status != PAM_SUCCESS) + die("pam_acct_mgmt failed: %s", pam_strerror(pamh, status)); + status = pam_end(pamh, status); + if (status != PAM_SUCCESS) + die("pam_end failed: %s", pam_strerror(pamh, status)); + + /* If we get to here, the user successfully authenticated. */ + return true; +} + + +/* +** Try to get a password out of a dbm file. The dbm file should have the +** username for the key and the crypted password as the value. The crypted +** password, if found, is returned as a newly allocated string; otherwise, +** NULL is returned. +*/ +#if !(defined(HAVE_DBM) || defined(HAVE_BDB_DBM)) +static char * +password_dbm(char *user UNUSED, const char *file UNUSED) +{ + return NULL; +} +#else +static char * +password_dbm(char *name, const char *file) +{ + datum key, value; + DBM *database; + char *password; + + database = dbm_open(file, O_RDONLY, 0600); + if (database == NULL) + return NULL; + key.dptr = name; + key.dsize = strlen(name); + value = dbm_fetch(database, key); + if (value.dptr == NULL) { + dbm_close(database); + return NULL; + } + password = xmalloc(value.dsize + 1); + strlcpy(password, value.dptr, value.dsize + 1); + dbm_close(database); + return password; +} +#endif /* HAVE_DBM || HAVE_BDB_DBM */ + + +/* +** Try to get a password out of the system /etc/shadow file. The crypted +** password, if found, is returned as a newly allocated string; otherwise, +** NULL is returned. +*/ +#if !HAVE_GETSPNAM +static char * +password_shadow(const char *user UNUSED) +{ + return NULL; +} +#else +static char * +password_shadow(const char *user) +{ + struct spwd *spwd; + + spwd = getspnam(user); + if (spwd != NULL) + return xstrdup(spwd->sp_pwdp); + return NULL; +} +#endif /* HAVE_GETSPNAM */ + + +/* +** Try to get a password out of the system password file. The crypted +** password, if found, is returned as a newly allocated string; otherwise, +** NULL is returned. +*/ +static char * +password_system(const char *username) +{ + struct passwd *pwd; + + pwd = getpwnam(username); + if (pwd != NULL) + return xstrdup(pwd->pw_passwd); + return NULL; +} + + +/* +** Try to get the name of a user's primary group out of the system group +** file. The group, if found, is returned as a newly allocated string; +** otherwise, NULL is returned. If the username is not found, NULL is +** returned. +*/ +static char * +group_system(const char *username) +{ + struct passwd *pwd; + struct group *gr; + + pwd = getpwnam(username); + if (pwd == NULL) + return NULL; + gr = getgrgid(pwd->pw_gid); + if (gr == NULL) + return NULL; + return xstrdup(gr->gr_name); +} + + +/* +** Output username (and group, if desired) in correct return format. +*/ +static void +output_user(const char *username, bool wantgroup) +{ + if (wantgroup) { + char *group = group_system(username); + if (group == NULL) + die("group info for user %s not available", username); + printf("User:%s@%s\n", username, group); + } + else + printf("User:%s\n", username); +} + + +/* +** Main routines. +** +** We handle the variences between systems with #if blocks above, so that +** this code can look fairly clean. +*/ + +int +check_password(const char* username, const char* password) +{ + bool wantgroup = false; + struct auth_info *authinfo = NULL; + + authinfo = xmalloc(sizeof(struct auth_info)); + authinfo->username = username; + authinfo->password = password; + + if (auth_pam(authinfo->username, authinfo->password)) { + output_user(authinfo->username, wantgroup); + return 0; + } + password = password_system(authinfo->username); + if (password == NULL) + return 1; + if (strcmp(password, crypt(authinfo->password, password)) != 0) + return 1; + + /* The password matched. */ + output_user(authinfo->username, wantgroup); + return 0; +}
\ No newline at end of file diff --git a/src/ckpasswd.c b/src/ckpasswd.c index 04b4b64..e3fed82 100644 --- a/src/ckpasswd.c +++ b/src/ckpasswd.c @@ -252,11 +252,12 @@ output_user(const char *username, bool wantgroup) /* -** Main routine. +** Main routines. ** ** We handle the variences between systems with #if blocks above, so that ** this code can look fairly clean. */ + int main(int argc, char *argv[]) { diff --git a/src/smartauthmon.cpp b/src/smartauthmon.cpp index 5b8d029..cb21f21 100644 --- a/src/smartauthmon.cpp +++ b/src/smartauthmon.cpp @@ -40,9 +40,19 @@ // The [secure] temporary directory for authentication #define SECURE_DIRECTORY_PATH "/tmp/smartauth" +// The Trinity binary directory +#define TRINITY_BIN_PREFIX "/opt/trinity/bin/" + // Some internal constants #define CREATE_LIFE_CYCLE "01" +#define tqarg arg + +// In ckpass.o +extern "C" { + int check_password(const char* username, const char* password); +} + static TQString secure_directory; static TQString command_mode; static TQString select_file; @@ -251,6 +261,7 @@ int main (int argc, char *argv[]) { TQString smartcard_username; TQString oldsmartcard_username; + TQString smartcard_password; TQString smartcard_slave; TQString lverify; TQString cverify; @@ -295,6 +306,18 @@ int main (int argc, char *argv[]) return 1; } + // Read hexidecimal_key from the system crypto files + FILE* fpkey = fopen("/etc/smartauth/smartauthmon.key", "rb"); + if (fpkey == NULL) { + printf("Smart card login has been disabled. Exiting...\n\r"); + return 1; + } + else { + fclose(fpkey); + } + hexidecimal_key = readfile("/etc/smartauth/smartauthmon.key"); + hexidecimal_key.replace('\n', ""); + oldsmartcard_username=""; printf("[DEBUG 400.0] Ready...\n\r"); while (1) { @@ -371,9 +394,7 @@ int main (int argc, char *argv[]) // Now DES encrypt the challenge // Later, change the initialization vector to random if possible - // Read hexidecimal_key from the system crypto files and create the response from the challenge - hexidecimal_key = readfile("/etc/smartauth/smartauthmon.key"); - hexidecimal_key.replace('\n', ""); + // Create the response from the challenge systemexec((TQString("openssl des-ecb -in %1/challenge -out %2/response -K %3 -iv 1").tqarg(secure_directory).tqarg(secure_directory).tqarg(hexidecimal_key)).ascii()); if (command_mode == "acos") { @@ -414,15 +435,20 @@ int main (int argc, char *argv[]) // Get username and password TQString response = get_file("10 02", "text"); smartcard_username = readfile(response); + unlink(response.ascii()); response = get_file("10 03", "text"); - systemexec((TQString("mv %1 %2/password").tqarg(response).tqarg(secure_directory)).ascii()); + smartcard_password = readfile(response.ascii()); + unlink(response.ascii()); response = get_file("10 04", "text"); smartcard_slave = readfile(response); + unlink(response.ascii()); if (smartcard_slave == "SLAVE") { get_file("10 05", "text"); smartcard_minutes_raw = readfile(response); + unlink(response.ascii()); get_file("10 06", "text"); internet_minutes = readfile(response).toInt(); + unlink(response.ascii()); } } else { @@ -470,8 +496,8 @@ int main (int argc, char *argv[]) int errcode=0; int waserror=0; int noactivesessions=0; - - result = exec("/opt/trinity/bin/kdmctl -g list"); + + result = exec(TRINITY_BIN_PREFIX "kdmctl -g list"); if (result == "ok") { noactivesessions=1; result="okbutempty"; @@ -510,17 +536,12 @@ int main (int argc, char *argv[]) if (darray[index] != "") { printf("[DEBUG 400.a] Found existing session on desktop: %d\n\r", index); foundsession=1; - // Check password - // FIXME - // This might expose the password for an instant - // Integrate the password checking from "ckpasswd.c" here instead - lverify = exec((TQString("/usr/bin/smartauthckpasswd -u %1 -p $(cat %2/password)").tqarg(darray[index]).tqarg(secure_directory)).ascii()); - cverify = TQString("User:%1").tqarg(darray[index]); udisplay = TQString(":%1").tqarg(index); - if (lverify == cverify) { - systemexec((TQString("su %1 -c \"export DISPLAY=%2; /opt/trinity/bin/dcop kdesktop KScreensaverIface quit\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); - systemexec((TQString("su %1 -c \"export DISPLAY=%2; /opt/trinity/bin/dcop kdesktop KScreensaverIface enable false\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); - systemexec((TQString("/opt/trinity/bin/kdmctl activate %1").tqarg(udisplay)).ascii()); + // Check password + if (check_password(smartcard_username.ascii(), smartcard_password.ascii()) == 0) { + systemexec((TQString("su %1 -c \"export DISPLAY=%2; " TRINITY_BIN_PREFIX "dcop kdesktop KScreensaverIface quit\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); + systemexec((TQString("su %1 -c \"export DISPLAY=%2; " TRINITY_BIN_PREFIX "dcop kdesktop KScreensaverIface enable false\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); + systemexec((TQString(TRINITY_BIN_PREFIX "kdmctl activate %1").tqarg(udisplay)).ascii()); } else { systemexec("echo \"EUnauthorized SmartCard Inserted\" > /tmp/ksocket-global/kdesktoplockcontrol &"); @@ -580,10 +601,16 @@ int main (int argc, char *argv[]) newdisplay = TQString(":%1").tqarg(newdisplayint); printf("[DEBUG 400.f] The next display to start will be %s\n\r", newdisplay.ascii()); - systemexec("/opt/trinity/bin/kdmctl -g reserve"); - systemexec((TQString("/opt/trinity/bin/kdmctl -g login %1 now %2 $(cat %3/password)").tqarg(newdisplay).tqarg(smartcard_username).tqarg(secure_directory)).ascii()); + systemexec(TRINITY_BIN_PREFIX "kdmctl -g reserve"); + TQString kdmctl_command = TQString("login\t%1\tnow\t%2\t%3\n").tqarg(newdisplay).tqarg(smartcard_username).tqarg(smartcard_password); + FILE* kdmctlpipe = popen(TRINITY_BIN_PREFIX "kdmctl -g -", "w"); + if (pipe) { + fputs(kdmctl_command.ascii(), kdmctlpipe); + fflush(kdmctlpipe); + pclose(kdmctlpipe); + } sleep(2); - systemexec((TQString("/opt/trinity/bin/kdmctl -g activate %1").tqarg(newdisplay)).ascii()); + systemexec((TQString(TRINITY_BIN_PREFIX "kdmctl -g activate %1").tqarg(newdisplay)).ascii()); udisplay=newdisplay; } @@ -603,8 +630,8 @@ int main (int argc, char *argv[]) while (output == 0) { sleep(1); - systemexec((TQString("su %1 -c \"export DISPLAY=%2; /opt/trinity/bin/dcop kdesktop KScreensaverIface quit\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); - systemexec((TQString("su %1 -c \"export DISPLAY=%2; /opt/trinity/bin/dcop kdesktop KScreensaverIface enable false\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); + systemexec((TQString("su %1 -c \"export DISPLAY=%2; " TRINITY_BIN_PREFIX "dcop kdesktop KScreensaverIface quit\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); + systemexec((TQString("su %1 -c \"export DISPLAY=%2; " TRINITY_BIN_PREFIX "dcop kdesktop KScreensaverIface enable false\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); output = systemexec("echo \"exit\" | scriptor 2>/dev/null 1>/dev/null"); if (smartcard_slave == "SLAVE") { timer--; @@ -649,7 +676,7 @@ int main (int argc, char *argv[]) result="ok"; timeout=0; errcode=0; - result = exec("/opt/trinity/bin/kdmctl -g list"); + result = exec(TRINITY_BIN_PREFIX "kdmctl -g list"); if (result == "ok") { noactivesessions=1; result="okbutempty"; @@ -684,14 +711,16 @@ int main (int argc, char *argv[]) timeout=0; blankresult=""; while (blankresult != "true") { - systemexec((TQString("/opt/trinity/bin/kdmctl -g activate %1").tqarg(udisplay)).ascii()); - systemexec((TQString("su %1 -c \"export DISPLAY=%2; /opt/trinity/bin/dcop kdesktop KScreensaverIface enable true\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); - systemexec((TQString("su %1 -c \"export DISPLAY=%2; /opt/trinity/bin/dcop kdesktop KScreensaverIface lock\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); + systemexec((TQString(TRINITY_BIN_PREFIX "kdmctl -g activate %1").tqarg(udisplay)).ascii()); + systemexec((TQString("su %1 -c \"export DISPLAY=%2; " TRINITY_BIN_PREFIX "dcop kdesktop KScreensaverIface enable true\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); + systemexec((TQString("su %1 -c \"export DISPLAY=%2; " TRINITY_BIN_PREFIX "dcop kdesktop KScreensaverIface lock\"").tqarg(smartcard_username).tqarg(udisplay)).ascii()); int retcode; - blankresult = execret(TQString("su %1 -c \"export DISPLAY=%2; /opt/trinity/bin/dcop kdesktop KScreensaverIface isBlanked\"").tqarg(smartcard_username).tqarg(udisplay).ascii(), &retcode); + blankresult = execret(TQString("su %1 -c \"export DISPLAY=%2; " TRINITY_BIN_PREFIX "dcop kdesktop KScreensaverIface isBlanked\"").tqarg(smartcard_username).tqarg(udisplay).ascii(), &retcode); if (retcode != 0) { blankresult="true"; } + blankresult = blankresult.replace('\n', ""); + logouttest = exec((TQString("echo %1 | grep 'target display has no VT assigned'").tqarg(blankresult)).ascii()); if (logouttest != "") { printf("[DEBUG 401.6] User has logged out\n\r"); |