diff options
Diffstat (limited to 'login-utils/cryptocard.c')
-rw-r--r-- | login-utils/cryptocard.c | 222 |
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 */ |