perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[freeradius.git] / src / modules / rlm_x99_token / x99_util.c
1 /*
2  * x99_util.c
3  * $Id$
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * Copyright 2001,2002  Google, Inc.
20  */
21
22 #ifdef FREERADIUS
23 #include "radiusd.h"
24 #endif
25 #include "x99.h"
26
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <openssl/des.h> /* des_cblock */
36
37
38 static const char rcsid[] = "$Id$";
39
40
41 /* Card name to int mappings */
42 static struct {
43     const char *name;
44     uint32_t id;
45 } card[] = {
46     { "x9.9",             CRYPTOCARD_H8_RC },
47     { "generic",          CRYPTOCARD_H8_RC },
48
49     { "cryptocard-h8-rc", CRYPTOCARD_H8_RC },
50     { "cryptocard-d8-rc", CRYPTOCARD_D8_RC },
51     { "cryptocard-h7-rc", CRYPTOCARD_H7_RC },
52     { "cryptocard-d7-rc", CRYPTOCARD_D7_RC },
53     { "cryptocard-h8-es", CRYPTOCARD_H8_ES },
54     { "cryptocard-d8-es", CRYPTOCARD_D8_ES },
55     { "cryptocard-h7-es", CRYPTOCARD_H7_ES },
56     { "cryptocard-d7-es", CRYPTOCARD_D7_ES },
57     { "cryptocard-h8-rs", CRYPTOCARD_H8_RS },
58     { "cryptocard-d8-rs", CRYPTOCARD_D8_RS },
59     { "cryptocard-h7-rs", CRYPTOCARD_H7_RS },
60     { "cryptocard-d7-rs", CRYPTOCARD_D7_RS },
61
62     { NULL, 0 }                         /* end of list */
63 };
64
65
66 /*
67  * Return a random challenge.
68  * fd must be either -1 or an open fd to the random device.
69  * challenge is filled in on successful return (must be size len+1).
70  * Returns 0 on success, -1 on failure.
71  */
72 int
73 x99_get_challenge(int fd, char *challenge, int len)
74 {
75     unsigned char rawchallenge[MAX_CHALLENGE_LEN];
76     int i;
77
78     if (fd == -1) {
79         if ((fd = open(DEVURANDOM, O_RDONLY)) == -1) {
80             x99_log(X99_LOG_ERR, "error opening %s: %s", DEVURANDOM,
81                     strerror(errno));
82             return -1;
83         }
84     }
85
86     if (x99_get_random(fd, rawchallenge, len) == -1) {
87         x99_log(X99_LOG_ERR, "failed to obtain random data");
88         return -1;
89     }
90     /* Convert the raw bytes to a decimal string. */
91     for (i = 0; i < len; ++i)
92         challenge[i] = '0' + rawchallenge[i] % 10;
93     challenge[i] = '\0';
94
95     return 0;
96 }
97
98 /*
99  * Return some number of random bytes.
100  * rnd_data must be allocated by the caller.
101  * Returns 0 on success, -1 on failure, rnd_data is filled in.
102  */
103 int
104 x99_get_random(int fd, unsigned char *rnd_data, int req_bytes)
105 {
106     int bytes_read = 0;
107
108     while (bytes_read < req_bytes) {
109         int n;
110
111         n = read(fd, rnd_data + bytes_read, req_bytes - bytes_read);
112         if (n <= 0) {
113             x99_log(X99_LOG_ERR, "x99_get_random: error reading from %s: %s",
114                     DEVURANDOM, strerror(errno));
115             return -1;
116         }
117         bytes_read += n;
118     }
119
120     return 0;
121 }
122
123
124 /*
125  * Convert the ASCII string representation of a DES key to raw octets.
126  * keyblock is filled in.  Returns 0 on success, -1 otherwise.
127  */
128 int
129 x99_string_to_keyblock(const char *s, des_cblock keyblock)
130 {
131     int i;
132
133     if (s == NULL || strlen(s) < 16)
134         return -1;
135
136     /*
137      * We could just use sscanf, but we do this a lot, and have very
138      * specific needs, and it's easy to implement, so let's go for it!
139      */
140     for (i = 0; i < 8; ++i) {
141         unsigned int n[2];
142
143         n[0] = *s++ - '0';
144         n[1] = *s++ - '0';
145         if (n[0] > 9) {
146             n[0] -= 'a' - '9' - 1;
147         }
148         if (n[1] > 9) {
149             n[1] -= 'a' - '9' - 1;
150         }
151
152         keyblock[i]  = n[0] << 4;
153         keyblock[i] += n[1];
154     }
155     return 0;
156 }
157
158
159 /* Character maps for generic hex and vendor specific decimal modes */
160 const char x99_hex_conversion[]         = "0123456789abcdef";
161 const char x99_cc_dec_conversion[]      = "0123456789012345";
162 const char x99_snk_dec_conversion[]     = "0123456789222333";
163 const char x99_sc_friendly_conversion[] = "0123456789ahcpef";
164
165 /*
166  * Convert a DES keyblock to an ASCII string.
167  * Fills in s, which must point to at least 17 bytes of space.
168  * Note that each octet expands into 2 hex digits in ASCII (0xAA -> 0x4141);
169  * add a NULL string terminator and you get the 17 byte requirement.
170  */
171 void
172 x99_keyblock_to_string(char *s, const des_cblock keyblock,
173                        const char conversion[17])
174 {
175     int i;
176
177     for (i = 0; i < 8; ++i) {
178         unsigned n[2];
179
180         n[0] = (keyblock[i] >> 4) & 0x0f;
181         n[1] = keyblock[i] & 0x0f;
182         s[2 * i + 0] = conversion[n[0]];
183         s[2 * i + 1] = conversion[n[1]];
184     }
185     s[16] = '\0';
186 }
187
188
189 /*
190  * fillin user_info from our database (key file)
191  * returns 0 on success, -1 for user not found, -2 for other errors.
192  */
193 int
194 x99_get_user_info(const char *pwdfile, const char *username,
195                   x99_user_info_t *user_info)
196 {
197     FILE *fp;
198     char s[80];
199     char *p, *q;
200     int found, i;
201     struct stat st;
202
203     /* Verify permissions first. */
204     if (stat(pwdfile, &st) != 0) {
205         x99_log(X99_LOG_ERR, "x99_get_user_info: pwdfile %s error: %s",
206                 pwdfile, strerror(errno));
207         return -2;
208     }
209     if ((st.st_mode & (S_IXUSR|S_IRWXG|S_IRWXO)) != 0) {
210         x99_log(X99_LOG_ERR,
211                 "x99_get_user_info: pwdfile %s has loose permissions", pwdfile);
212         return -2;
213     }
214
215     if ((fp = fopen(pwdfile, "r")) == NULL) {
216         x99_log(X99_LOG_ERR, "x99_get_user_info: error opening %s: %s",
217                 pwdfile, strerror(errno));
218         return -2;
219     }
220
221     /*
222      * Find the requested user.
223      * Add a ':' to the username to make sure we don't match shortest prefix.
224      */
225     p = malloc(strlen(username) + 2);
226     if (!p) {
227         x99_log(X99_LOG_CRIT, "x99_get_user_info: out of memory");
228         return -2;
229     }
230     (void) sprintf(p, "%s:", username);
231     found = 0;
232     while (!feof(fp)) {
233         if (fgets(s, sizeof(s), fp) == NULL) {
234             if (!feof(fp)) {
235                 x99_log(X99_LOG_ERR,
236                         "x99_get_user_info: error reading from %s: %s",
237                         pwdfile, strerror(errno));
238                 (void) fclose(fp);
239                 free(p);
240                 return -2;
241             }
242         } else if (!strncmp(s, p, strlen(p))) {
243             found = 1;
244             break;
245         }
246     }
247     (void) fclose(fp);
248     free(p);
249     if (!found) {
250 #if 0
251         /* Noisy ... let the caller report this. */
252         x99_log(X99_LOG_AUTH, "x99_get_user_info: [%s] not found in %s",
253                 username, pwdfile);
254 #endif
255         return -1;
256     }
257
258     /* Found him, skip to next field (card). */
259     if ((p = strchr(s, ':')) == NULL) {
260         x99_log(X99_LOG_ERR,
261                 "x99_get_user_info: invalid format for [%s] in %s",
262                 username, pwdfile);
263         return -2;
264     }
265     p++;
266     /* strtok() */
267     if ((q = strchr(p, ':')) == NULL) {
268         x99_log(X99_LOG_ERR,
269                 "x99_get_user_info: invalid format for [%s] in %s",
270                 username, pwdfile);
271         return -2;
272     }
273     *q++ = '\0';
274     /* p: card_type, q: key */
275
276     /* Match against card types. */
277     found = 0;
278     for (i = 0; card[i].name; ++i) {
279         if (!strcasecmp(p, card[i].name)) {
280             found = 1;
281             user_info->card_id = card[i].id;
282             break;
283         }
284     }
285     if (!found) {
286         x99_log(X99_LOG_ERR,
287                 "x99_get_user_info: unknown card %s for [%s] in %s",
288                 p, username, pwdfile);
289         return -2;
290     }
291
292     if (!(strlen(q) == 16 || (strlen(q) == 17 && q[16] == '\n'))) {
293         /* 8 octets + possible trailing newline */
294         x99_log(X99_LOG_ERR, "x99_get_user_info: invalid key for [%s] in %s",
295                 username, pwdfile);
296         return -2;
297     }
298
299     /* Convert the key from ASCII to a keyblock. (+translate error code) */
300     return x99_string_to_keyblock(q, user_info->keyblock) * -2;
301 }
302