summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-09-20 20:01:11 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-09-20 20:01:11 +0000
commit32b6f4c4aeddfdda9343d59fba02ae7fe3e0b24c (patch)
treeede5064d9754d063fecece08d81f8ef1e98cb3ba /src
parent125b13c1760df7ad557d0d5462b39c7f092e2f3b (diff)
downloadsmartcardauth-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/Makefile13
-rw-r--r--src/ckpass.c284
-rw-r--r--src/ckpasswd.c3
-rw-r--r--src/smartauthmon.cpp81
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");