/* * x99_util.c * $Id: x99_util.c,v 1.11 2002/10/31 10:01:23 fcusack Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2001,2002 Google, Inc. */ #ifdef FREERADIUS #include "radiusd.h" #endif #include "x99.h" #include #include #include #include #include #include #include #include #include /* des_cblock */ static const char rcsid[] = "$Id: x99_util.c,v 1.11 2002/10/31 10:01:23 fcusack Exp $"; /* Card name to int mappings */ static struct { const char *name; uint32_t id; } card[] = { { "x9.9", CRYPTOCARD_H8_RC }, { "generic", CRYPTOCARD_H8_RC }, { "cryptocard-h8-rc", CRYPTOCARD_H8_RC }, { "cryptocard-d8-rc", CRYPTOCARD_D8_RC }, { "cryptocard-h7-rc", CRYPTOCARD_H7_RC }, { "cryptocard-d7-rc", CRYPTOCARD_D7_RC }, { "cryptocard-h8-es", CRYPTOCARD_H8_ES }, { "cryptocard-d8-es", CRYPTOCARD_D8_ES }, { "cryptocard-h7-es", CRYPTOCARD_H7_ES }, { "cryptocard-d7-es", CRYPTOCARD_D7_ES }, { "cryptocard-h8-rs", CRYPTOCARD_H8_RS }, { "cryptocard-d8-rs", CRYPTOCARD_D8_RS }, { "cryptocard-h7-rs", CRYPTOCARD_H7_RS }, { "cryptocard-d7-rs", CRYPTOCARD_D7_RS }, { NULL, 0 } /* end of list */ }; /* * Return a random challenge. * fd must be either -1 or an open fd to the random device. * challenge is filled in on successful return (must be size len+1). * Returns 0 on success, -1 on failure. */ int x99_get_challenge(int fd, char *challenge, int len) { unsigned char rawchallenge[MAX_CHALLENGE_LEN]; int i; if (fd == -1) { if ((fd = open(DEVURANDOM, O_RDONLY)) == -1) { x99_log(X99_LOG_ERR, "error opening %s: %s", DEVURANDOM, strerror(errno)); return -1; } } if (x99_get_random(fd, rawchallenge, len) == -1) { x99_log(X99_LOG_ERR, "failed to obtain random data"); return -1; } /* Convert the raw bytes to a decimal string. */ for (i = 0; i < len; ++i) challenge[i] = '0' + rawchallenge[i] % 10; challenge[i] = '\0'; return 0; } /* * Return some number of random bytes. * rnd_data must be allocated by the caller. * Returns 0 on success, -1 on failure, rnd_data is filled in. */ int x99_get_random(int fd, unsigned char *rnd_data, int req_bytes) { int bytes_read = 0; while (bytes_read < req_bytes) { int n; n = read(fd, rnd_data + bytes_read, req_bytes - bytes_read); if (n <= 0) { x99_log(X99_LOG_ERR, "x99_get_random: error reading from %s: %s", DEVURANDOM, strerror(errno)); return -1; } bytes_read += n; } return 0; } /* * Convert the ASCII string representation of a DES key to raw octets. * keyblock is filled in. Returns 0 on success, -1 otherwise. */ int x99_string_to_keyblock(const char *s, des_cblock keyblock) { int i; if (s == NULL || strlen(s) < 16) return -1; /* * We could just use sscanf, but we do this a lot, and have very * specific needs, and it's easy to implement, so let's go for it! */ for (i = 0; i < 8; ++i) { unsigned int n[2]; n[0] = *s++ - '0'; n[1] = *s++ - '0'; if (n[0] > 9) { n[0] -= 'a' - '9' - 1; } if (n[1] > 9) { n[1] -= 'a' - '9' - 1; } keyblock[i] = n[0] << 4; keyblock[i] += n[1]; } return 0; } /* Character maps for generic hex and vendor specific decimal modes */ const char x99_hex_conversion[] = "0123456789abcdef"; const char x99_cc_dec_conversion[] = "0123456789012345"; const char x99_snk_dec_conversion[] = "0123456789222333"; const char x99_sc_friendly_conversion[] = "0123456789ahcpef"; /* * Convert a DES keyblock to an ASCII string. * Fills in s, which must point to at least 17 bytes of space. * Note that each octet expands into 2 hex digits in ASCII (0xAA -> 0x4141); * add a NULL string terminator and you get the 17 byte requirement. */ void x99_keyblock_to_string(char *s, const des_cblock keyblock, const char conversion[17]) { int i; for (i = 0; i < 8; ++i) { unsigned n[2]; n[0] = (keyblock[i] >> 4) & 0x0f; n[1] = keyblock[i] & 0x0f; s[2 * i + 0] = conversion[n[0]]; s[2 * i + 1] = conversion[n[1]]; } s[16] = '\0'; } /* * fillin user_info from our database (key file) * returns 0 on success, -1 for user not found, -2 for other errors. */ int x99_get_user_info(const char *pwdfile, const char *username, x99_user_info_t *user_info) { FILE *fp; char s[80]; char *p, *q; int found, i; struct stat st; /* Verify permissions first. */ if (stat(pwdfile, &st) != 0) { x99_log(X99_LOG_ERR, "x99_get_user_info: pwdfile %s error: %s", pwdfile, strerror(errno)); return -2; } if ((st.st_mode & (S_IXUSR|S_IRWXG|S_IRWXO)) != 0) { x99_log(X99_LOG_ERR, "x99_get_user_info: pwdfile %s has loose permissions", pwdfile); return -2; } if ((fp = fopen(pwdfile, "r")) == NULL) { x99_log(X99_LOG_ERR, "x99_get_user_info: error opening %s: %s", pwdfile, strerror(errno)); return -2; } /* * Find the requested user. * Add a ':' to the username to make sure we don't match shortest prefix. */ p = malloc(strlen(username) + 2); if (!p) { x99_log(X99_LOG_CRIT, "x99_get_user_info: out of memory"); return -2; } (void) sprintf(p, "%s:", username); found = 0; while (!feof(fp)) { if (fgets(s, sizeof(s), fp) == NULL) { if (!feof(fp)) { x99_log(X99_LOG_ERR, "x99_get_user_info: error reading from %s: %s", pwdfile, strerror(errno)); (void) fclose(fp); free(p); return -2; } } else if (!strncmp(s, p, strlen(p))) { found = 1; break; } } (void) fclose(fp); free(p); if (!found) { #if 0 /* Noisy ... let the caller report this. */ x99_log(X99_LOG_AUTH, "x99_get_user_info: [%s] not found in %s", username, pwdfile); #endif return -1; } /* Found him, skip to next field (card). */ if ((p = strchr(s, ':')) == NULL) { x99_log(X99_LOG_ERR, "x99_get_user_info: invalid format for [%s] in %s", username, pwdfile); return -2; } p++; /* strtok() */ if ((q = strchr(p, ':')) == NULL) { x99_log(X99_LOG_ERR, "x99_get_user_info: invalid format for [%s] in %s", username, pwdfile); return -2; } *q++ = '\0'; /* p: card_type, q: key */ /* Match against card types. */ found = 0; for (i = 0; card[i].name; ++i) { if (!strcasecmp(p, card[i].name)) { found = 1; user_info->card_id = card[i].id; break; } } if (!found) { x99_log(X99_LOG_ERR, "x99_get_user_info: unknown card %s for [%s] in %s", p, username, pwdfile); return -2; } if (!(strlen(q) == 16 || (strlen(q) == 17 && q[16] == '\n'))) { /* 8 octets + possible trailing newline */ x99_log(X99_LOG_ERR, "x99_get_user_info: invalid key for [%s] in %s", username, pwdfile); return -2; } /* Convert the key from ASCII to a keyblock. (+translate error code) */ return x99_string_to_keyblock(q, user_info->keyblock) * -2; }