Merge from vendor branch OPENSSH:
[dragonfly.git] / contrib / hostapd-0.4.9 / eap_sim_db.c
1 /*
2  * hostapd / EAP-SIM database/authenticator gateway
3  * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi>
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 version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 /* This is an example implementation of the EAP-SIM database/authentication
16  * gateway interface that is expected to be replaced with an implementation of
17  * SS7 gateway to GSM authentication center (HLR/AuC) or a local
18  * implementation of SIM triplet generator.
19  *
20  * The example implementation here reads triplets from a text file in
21  * IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex strings. This
22  * is used to simulate an HLR/AuC. As such, it is not very useful for real life
23  * authentication, but it is useful both as an example implementation and for
24  * EAP-SIM testing.
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "common.h"
32 #include "eap_sim_common.h"
33 #include "eap_sim_db.h"
34
35
36 /* TODO: add an alternative callback based version of the interface. This is
37  * needed to work better with the single threaded design of hostapd. For this,
38  * the EAP data has to be stored somewhere and eap_sim_db is given a context
39  * pointer for this and a callback function. The callback function will re-send
40  * the EAP data through normal operations which will eventually end up calling
41  * eap_sim_db_get_gsm_triplets() again for the same user. This time, eap_sim_db
42  * should have the triplets available immediately. */
43
44
45 struct eap_sim_db_data {
46         char *fname;
47 };
48
49 #define KC_LEN 8
50 #define SRES_LEN 4
51 #define RAND_LEN 16
52
53
54 /* Initialize EAP-SIM database/authentication gateway interface.
55  * Returns pointer to a private data structure. */
56 void * eap_sim_db_init(const char *config)
57 {
58         struct eap_sim_db_data *data;
59
60         data = malloc(sizeof(*data));
61         if (data == NULL) {
62                 return NULL;
63         }
64
65         memset(data, 0, sizeof(*data));
66         data->fname = strdup(config);
67         if (data->fname == NULL) {
68                 free(data);
69                 return NULL;
70         }
71
72         return data;
73 }
74
75 /* Deinitialize EAP-SIM database/authentication gateway interface.
76  * priv is the pointer from eap_sim_db_init(). */
77 void eap_sim_db_deinit(void *priv)
78 {
79         struct eap_sim_db_data *data = priv;
80         free(data->fname);
81         free(data);
82 }
83
84
85 /* Get GSM triplets for user name identity (identity_len bytes). In most cases,
86  * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format.
87  * The identity may also include NAI realm (@realm).
88  * priv is the pointer from eap_sim_db_init().
89  * Returns the number of triplets received (has to be less than or equal to
90  * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are
91  * pointers to data areas for the triplets. */
92 int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
93                                 size_t identity_len, int max_chal,
94                                 u8 *rand, u8 *kc, u8 *sres)
95 {
96         struct eap_sim_db_data *data = priv;
97         FILE *f;
98         int count, i;
99         char buf[80], *pos, *next;
100
101         f = fopen(data->fname, "r");
102         if (f == NULL) {
103                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet "
104                            "file '%s'", data->fname);
105                 return -1;
106         }
107
108         if (identity_len < 2 || identity[0] != '1') {
109                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
110                                   identity, identity_len);
111                 fclose(f);
112                 return -1;
113         }
114         identity++;
115         identity_len--;
116         for (i = 0; i < identity_len; i++) {
117                 if (identity[i] == '@') {
118                         identity_len = i;
119                         break;
120                 }
121         }
122         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: get triplets for IMSI",
123                           identity, identity_len);
124
125         count = 0;
126         while (count < max_chal && fgets(buf, sizeof(buf), f)) {
127                 /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */
128                 buf[sizeof(buf) - 1] = '\0';
129                 pos = buf;
130                 while (*pos != '\0' && *pos != '\n')
131                         pos++;
132                 if (*pos == '\n')
133                         *pos = '\0';
134                 if (pos - buf < 60 || pos[0] == '#')
135                         continue;
136
137                 pos = strchr(buf, ':');
138                 if (pos == NULL)
139                         continue;
140                 *pos++ = '\0';
141                 if (strlen(buf) != identity_len ||
142                     memcmp(buf, identity, identity_len) != 0)
143                         continue;
144
145                 next = strchr(pos, ':');
146                 if (next == NULL)
147                         continue;
148                 *next++ = '\0';
149                 if (hexstr2bin(pos, &kc[count * KC_LEN], KC_LEN) < 0)
150                         continue;
151
152                 pos = next;
153                 next = strchr(pos, ':');
154                 if (next == NULL)
155                         continue;
156                 *next++ = '\0';
157                 if (hexstr2bin(pos, &sres[count * SRES_LEN], SRES_LEN) < 0)
158                         continue;
159
160                 if (hexstr2bin(next, &rand[count * RAND_LEN], RAND_LEN) < 0)
161                         continue;
162
163                 count++;
164         }
165
166         fclose(f);
167
168         if (count == 0) {
169                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: no triplets found");
170                 count = -1;
171         }
172
173         return count;
174 }
175
176
177 /* Verify whether the given user identity (identity_len bytes) is known. In
178  * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
179  * ASCII format.
180  * priv is the pointer from eap_sim_db_init().
181  * Returns 0 if the user is found and GSM triplets would be available for it or
182  * -1 on error (e.g., user not found or no triplets available). */
183 int eap_sim_db_identity_known(void *priv, const u8 *identity,
184                               size_t identity_len)
185 {
186         struct eap_sim_db_data *data = priv;
187         FILE *f;
188         char buf[80], *pos;
189         int i;
190
191         if (identity_len < 1 || identity[0] != '1') {
192                 return -1;
193         }
194
195         f = fopen(data->fname, "r");
196         if (f == NULL) {
197                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet "
198                            "file '%s'", data->fname);
199                 return -1;
200         }
201
202         if (identity_len < 2 || identity[0] != '1') {
203                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
204                                   identity, identity_len);
205                 return -1;
206         }
207         identity++;
208         identity_len--;
209         for (i = 0; i < identity_len; i++) {
210                 if (identity[i] == '@') {
211                         identity_len = i;
212                         break;
213                 }
214         }
215
216         while (fgets(buf, sizeof(buf), f)) {
217                 /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */
218                 buf[sizeof(buf) - 1] = '\0';
219                 pos = buf;
220                 while (*pos != '\0' && *pos != '\n')
221                         pos++;
222                 if (*pos == '\n')
223                         *pos = '\0';
224                 if (pos - buf < 60 || pos[0] == '#')
225                         continue;
226
227                 pos = strchr(buf, ':');
228                 if (pos == NULL)
229                         continue;
230                 *pos++ = '\0';
231                 if (strlen(buf) != identity_len ||
232                     memcmp(buf, identity, identity_len) != 0)
233                         continue;
234
235                 fclose(f);
236                 return 0;
237         }
238
239         /* IMSI not found */
240
241         fclose(f);
242         return -1;
243 }