summaryrefslogtreecommitdiffstats
path: root/login-utils/cryptocard.c
diff options
context:
space:
mode:
Diffstat (limited to 'login-utils/cryptocard.c')
-rw-r--r--login-utils/cryptocard.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/login-utils/cryptocard.c b/login-utils/cryptocard.c
new file mode 100644
index 000000000..e735c7273
--- /dev/null
+++ b/login-utils/cryptocard.c
@@ -0,0 +1,222 @@
+/* cryptocard.c - support for the CRYPTOCard
+ RB-1 Challenge-Response Token, initial code by
+ bentson@grieg.seaslug.org (Randolph Bentson) on 3-Dec-96,
+ Hacked severely by poe@daimi.aau.dk.
+ This relies on an implementation of DES in a library, currently
+ it interfaces with the koontz-des.tar.gz implementation which
+ can be found in:
+
+ ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/des/
+
+ (Link with the fdes.o file from that distribution)
+
+ and with Eric A. Young's libdes implementation used in SSLeay. Also
+ available from the above ftp site. Link with the libdes.a library.
+
+ The sources for this code are maintained in
+
+ ftp://ftp.daimi.aau.dk/pub/linux/poe/poeigl-X.XX.tar.gz
+*/
+#ifdef CRYPTOCARD
+
+/******************** CONFIGURATION section *****************************/
+/*--------------- select ONE DES implementation ------------------------*/
+/*#define KOONTZ_DES */
+#define EAY_LIBDES
+/*--------------- define if on little endian machine (Intel x86) -------*/
+#define LITTLE_ENDIAN
+/******************** end of CONFIGURATION section **********************/
+
+#define _BSD_SOURCE
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#ifdef KOONTZ_DES
+#include "../koontz-des/des.h"
+#endif /* KOONTZ_DES */
+
+#ifdef EAY_LIBDES
+#include "../libdes/des.h"
+#endif /* EAY_LIBDES */
+
+extern char *getpass(const char *prompt);
+extern struct passwd *pwd;
+extern int timeout;
+
+static char *
+generate_challenge(void)
+{
+ static char challenge_str[30];
+ int rfd;
+ unsigned long clong;
+
+ /* create and present a challenge string */
+ if ((rfd = open("/dev/urandom", O_RDONLY)) < 0) {
+ syslog(LOG_NOTICE, "couldn't open /dev/urandom");
+ return NULL;
+ }
+ if (read(rfd, &clong, 4) < 4) {
+ close(rfd);
+ syslog(LOG_NOTICE, "couldn't read random data from /dev/urandom");
+ return NULL;
+ }
+ close(rfd);
+
+ sprintf(challenge_str,"%08lu", clong);
+ return challenge_str;
+}
+
+static char *
+get_key()
+{
+ int success = 0;
+ char keyfile[MAXPATHLEN];
+ static char key[10];
+ int rfd;
+ struct stat statbuf;
+
+ snprintf(keyfile, sizeof(keyfile), "%s/.cryptocard", pwd->pw_dir);
+
+ if ((rfd = open(keyfile, O_RDONLY)) < 0) {
+ syslog(LOG_NOTICE, "can't open %s for reading", keyfile);
+ goto bail_out;
+ }
+ if (fstat(rfd, &statbuf) < 0) {
+ syslog(LOG_NOTICE, "can't stat(%s)", keyfile);
+ goto close_and_bail_out;
+ }
+ if ((statbuf.st_uid != pwd->pw_uid)
+ || ((statbuf.st_mode & S_IFMT) != S_IFREG)
+ || (statbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) {
+ syslog(LOG_NOTICE, "%s doesn't have the correct filemodes", keyfile);
+ goto close_and_bail_out;
+ }
+
+ if (read(rfd, key, 8) < 8) {
+ syslog(LOG_NOTICE, "can't read data from %s", keyfile);
+ goto close_and_bail_out;
+ }
+
+ key[8] = 0;
+ success = 1;
+
+close_and_bail_out:
+ close(rfd);
+
+bail_out:
+ if (success)
+ return key;
+ else
+ return NULL;
+}
+
+static int
+check_response(char *challenge, char *response, char *key)
+{
+ char buf[20];
+
+#ifdef KOONTZ_DES
+ extern void des (union LR_block *);
+ extern void loadkey(char *,int);
+ extern void set_des_mode(int);
+
+ union LR_block data;
+
+ strncpy((char *)data.string, (char *)challenge, 8);
+ set_des_mode(ENCRYPT);
+ loadkey(key, NOSHIFT);
+ des(&data);
+
+ memset(key, 0, 8); /* no need for the secret key anymore, scratch it */
+
+ sprintf(buf, "%2.2X%2.2X%2.2X%2.2X",
+ (int)(data.LR[0]) & 0xff,
+ (int)(data.LR[0]>>8) & 0xff,
+ (int)(data.LR[0]>>16) & 0xff,
+ (int)(data.LR[0]>>24) & 0xff);
+#endif /* KOONTZ_DES */
+#ifdef EAY_LIBDES
+ des_cblock res;
+ des_key_schedule ks;
+
+ des_set_key((des_cblock *)key, ks);
+ memset(key, 0, 8);
+ des_ecb_encrypt((des_cblock *)challenge, &res, ks, DES_ENCRYPT);
+
+#ifdef LITTLE_ENDIAN
+ /* use this on Intel x86 boxes */
+ sprintf(buf, "%2.2X%2.2X%2.2X%2.2X",
+ res[0], res[1], res[2], res[3]);
+#else /* ie. BIG_ENDIAN */
+ /* use this on big endian RISC boxes */
+ sprintf(buf, "%2.2X%2.2X%2.2X%2.2X",
+ res[3], res[2], res[1], res[0]);
+#endif /* LITTLE_ENDIAN */
+#endif /* EAY_LIBDES */
+
+ /* return success only if ALL requirements have been met */
+ if (strncmp(buf, response, 8) == 0)
+ return 1;
+
+ return 0;
+}
+
+int
+cryptocard(void)
+{
+ char prompt[80];
+ char *challenge;
+ char *key;
+ char *response;
+
+ challenge = generate_challenge();
+ if (challenge == NULL) return 0;
+
+ snprintf(prompt, sizeof(prompt), "%s Password: ", challenge);
+
+ alarm((unsigned int)timeout); /* give user time to fiddle with card */
+ response = getpass(prompt); /* presents challenge and gets response */
+
+ if (response == NULL) return 0;
+
+ /* This requires some explanation: As root we may not be able to
+ read the directory of the user if it is on an NFS mounted
+ filesystem. We temporarily set our effective uid to the user-uid
+ making sure that we keep root privs. in the real uid.
+
+ A portable solution would require a fork(), but we rely on Linux
+ having the BSD setreuid() */
+
+ {
+ uid_t ruid = getuid();
+ gid_t egid = getegid();
+
+ setregid(-1, pwd->pw_gid);
+ setreuid(0, pwd->pw_uid);
+
+ /* now we can access the file */
+ /* get the (properly qualified) key */
+ key = get_key();
+
+ /* reset to root privs */
+ setuid(0); /* setreuid doesn't do it alone! */
+ setreuid(ruid, 0);
+ setregid(-1, egid);
+
+ if (key == NULL) return 0;
+ }
+
+ return check_response(challenge, response, key);
+}
+
+#endif /* CRYPTOCARD */