]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/iscsid/chap.c
Implement pci_enable_msi() and pci_disable_msi() in the LinuxKPI.
[FreeBSD/FreeBSD.git] / usr.sbin / iscsid / chap.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Edward Tomasz Napierala under sponsorship
8  * from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <assert.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <netinet/in.h>
40 #include <resolv.h>
41 #include <md5.h>
42
43 #include "iscsid.h"
44
45 static void
46 chap_compute_md5(const char id, const char *secret,
47     const void *challenge, size_t challenge_len, void *response,
48     size_t response_len)
49 {
50         MD5_CTX ctx;
51
52         assert(response_len == CHAP_DIGEST_LEN);
53
54         MD5Init(&ctx);
55         MD5Update(&ctx, &id, sizeof(id));
56         MD5Update(&ctx, secret, strlen(secret));
57         MD5Update(&ctx, challenge, challenge_len);
58         MD5Final(response, &ctx);
59 }
60
61 static int
62 chap_hex2int(const char hex)
63 {
64         switch (hex) {
65         case '0':
66                 return (0x00);
67         case '1':
68                 return (0x01);
69         case '2':
70                 return (0x02);
71         case '3':
72                 return (0x03);
73         case '4':
74                 return (0x04);
75         case '5':
76                 return (0x05);
77         case '6':
78                 return (0x06);
79         case '7':
80                 return (0x07);
81         case '8':
82                 return (0x08);
83         case '9':
84                 return (0x09);
85         case 'a':
86         case 'A':
87                 return (0x0a);
88         case 'b':
89         case 'B':
90                 return (0x0b);
91         case 'c':
92         case 'C':
93                 return (0x0c);
94         case 'd':
95         case 'D':
96                 return (0x0d);
97         case 'e':
98         case 'E':
99                 return (0x0e);
100         case 'f':
101         case 'F':
102                 return (0x0f);
103         default:
104                 return (-1);
105         }
106 }
107
108 static int
109 chap_b642bin(const char *b64, void **binp, size_t *bin_lenp)
110 {
111         char *bin;
112         int b64_len, bin_len;
113
114         b64_len = strlen(b64);
115         bin_len = (b64_len + 3) / 4 * 3;
116         bin = calloc(bin_len, 1);
117         if (bin == NULL)
118                 log_err(1, "calloc");
119
120         bin_len = b64_pton(b64, bin, bin_len);
121         if (bin_len < 0) {
122                 log_warnx("malformed base64 variable");
123                 free(bin);
124                 return (-1);
125         }
126         *binp = bin;
127         *bin_lenp = bin_len;
128         return (0);
129 }
130
131 /*
132  * XXX: Review this _carefully_.
133  */
134 static int
135 chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp)
136 {
137         int i, hex_len, nibble;
138         bool lo = true; /* As opposed to 'hi'. */
139         char *bin;
140         size_t bin_off, bin_len;
141
142         if (strncasecmp(hex, "0b", strlen("0b")) == 0)
143                 return (chap_b642bin(hex + 2, binp, bin_lenp));
144
145         if (strncasecmp(hex, "0x", strlen("0x")) != 0) {
146                 log_warnx("malformed variable, should start with \"0x\""
147                     " or \"0b\"");
148                 return (-1);
149         }
150
151         hex += strlen("0x");
152         hex_len = strlen(hex);
153         if (hex_len < 1) {
154                 log_warnx("malformed variable; doesn't contain anything "
155                     "but \"0x\"");
156                 return (-1);
157         }
158
159         bin_len = hex_len / 2 + hex_len % 2;
160         bin = calloc(bin_len, 1);
161         if (bin == NULL)
162                 log_err(1, "calloc");
163
164         bin_off = bin_len - 1;
165         for (i = hex_len - 1; i >= 0; i--) {
166                 nibble = chap_hex2int(hex[i]);
167                 if (nibble < 0) {
168                         log_warnx("malformed variable, invalid char \"%c\"",
169                             hex[i]);
170                         free(bin);
171                         return (-1);
172                 }
173
174                 assert(bin_off < bin_len);
175                 if (lo) {
176                         bin[bin_off] = nibble;
177                         lo = false;
178                 } else {
179                         bin[bin_off] |= nibble << 4;
180                         bin_off--;
181                         lo = true;
182                 }
183         }
184
185         *binp = bin;
186         *bin_lenp = bin_len;
187         return (0);
188 }
189
190 #ifdef USE_BASE64
191 static char *
192 chap_bin2hex(const char *bin, size_t bin_len)
193 {
194         unsigned char *b64, *tmp;
195         size_t b64_len;
196
197         b64_len = (bin_len + 2) / 3 * 4 + 3; /* +2 for "0b", +1 for '\0'. */
198         b64 = malloc(b64_len);
199         if (b64 == NULL)
200                 log_err(1, "malloc");
201
202         tmp = b64;
203         tmp += sprintf(tmp, "0b");
204         b64_ntop(bin, bin_len, tmp, b64_len - 2);
205
206         return (b64);
207 }
208 #else
209 static char *
210 chap_bin2hex(const char *bin, size_t bin_len)
211 {
212         unsigned char *hex, *tmp, ch;
213         size_t hex_len;
214         size_t i;
215
216         hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */
217         hex = malloc(hex_len);
218         if (hex == NULL)
219                 log_err(1, "malloc");
220
221         tmp = hex;
222         tmp += sprintf(tmp, "0x");
223         for (i = 0; i < bin_len; i++) {
224                 ch = bin[i];
225                 tmp += sprintf(tmp, "%02x", ch);
226         }
227
228         return (hex);
229 }
230 #endif /* !USE_BASE64 */
231
232 struct chap *
233 chap_new(void)
234 {
235         struct chap *chap;
236
237         chap = calloc(1, sizeof(*chap));
238         if (chap == NULL)
239                 log_err(1, "calloc");
240
241         /*
242          * Generate the challenge.
243          */
244         arc4random_buf(chap->chap_challenge, sizeof(chap->chap_challenge));
245         arc4random_buf(&chap->chap_id, sizeof(chap->chap_id));
246
247         return (chap);
248 }
249
250 char *
251 chap_get_id(const struct chap *chap)
252 {
253         char *chap_i;
254         int ret;
255
256         ret = asprintf(&chap_i, "%d", chap->chap_id);
257         if (ret < 0)
258                 log_err(1, "asprintf");
259
260         return (chap_i);
261 }
262
263 char *
264 chap_get_challenge(const struct chap *chap)
265 {
266         char *chap_c;
267
268         chap_c = chap_bin2hex(chap->chap_challenge,
269             sizeof(chap->chap_challenge));
270
271         return (chap_c);
272 }
273
274 static int
275 chap_receive_bin(struct chap *chap, void *response, size_t response_len)
276 {
277
278         if (response_len != sizeof(chap->chap_response)) {
279                 log_debugx("got CHAP response with invalid length; "
280                     "got %zd, should be %zd",
281                     response_len, sizeof(chap->chap_response));
282                 return (1);
283         }
284
285         memcpy(chap->chap_response, response, response_len);
286         return (0);
287 }
288
289 int
290 chap_receive(struct chap *chap, const char *response)
291 {
292         void *response_bin;
293         size_t response_bin_len;
294         int error;
295
296         error = chap_hex2bin(response, &response_bin, &response_bin_len);
297         if (error != 0) {
298                 log_debugx("got incorrectly encoded CHAP response \"%s\"",
299                     response);
300                 return (1);
301         }
302
303         error = chap_receive_bin(chap, response_bin, response_bin_len);
304         free(response_bin);
305
306         return (error);
307 }
308
309 int
310 chap_authenticate(struct chap *chap, const char *secret)
311 {
312         char expected_response[CHAP_DIGEST_LEN];
313
314         chap_compute_md5(chap->chap_id, secret,
315             chap->chap_challenge, sizeof(chap->chap_challenge),
316             expected_response, sizeof(expected_response));
317
318         if (memcmp(chap->chap_response,
319             expected_response, sizeof(expected_response)) != 0) {
320                 return (-1);
321         }
322
323         return (0);
324 }
325
326 void
327 chap_delete(struct chap *chap)
328 {
329
330         free(chap);
331 }
332
333 struct rchap *
334 rchap_new(const char *secret)
335 {
336         struct rchap *rchap;
337
338         rchap = calloc(1, sizeof(*rchap));
339         if (rchap == NULL)
340                 log_err(1, "calloc");
341
342         rchap->rchap_secret = checked_strdup(secret);
343
344         return (rchap);
345 }
346
347 static void
348 rchap_receive_bin(struct rchap *rchap, const unsigned char id,
349     const void *challenge, size_t challenge_len)
350 {
351
352         rchap->rchap_id = id;
353         rchap->rchap_challenge = calloc(challenge_len, 1);
354         if (rchap->rchap_challenge == NULL)
355                 log_err(1, "calloc");
356         memcpy(rchap->rchap_challenge, challenge, challenge_len);
357         rchap->rchap_challenge_len = challenge_len;
358 }
359
360 int
361 rchap_receive(struct rchap *rchap, const char *id, const char *challenge)
362 {
363         unsigned char id_bin;
364         void *challenge_bin;
365         size_t challenge_bin_len;
366
367         int error;
368
369         id_bin = strtoul(id, NULL, 10);
370
371         error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len);
372         if (error != 0) {
373                 log_debugx("got incorrectly encoded CHAP challenge \"%s\"",
374                     challenge);
375                 return (1);
376         }
377
378         rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len);
379         free(challenge_bin);
380
381         return (0);
382 }
383
384 static void
385 rchap_get_response_bin(struct rchap *rchap,
386     void **responsep, size_t *response_lenp)
387 {
388         void *response_bin;
389         size_t response_bin_len = CHAP_DIGEST_LEN;
390
391         response_bin = calloc(response_bin_len, 1);
392         if (response_bin == NULL)
393                 log_err(1, "calloc");
394
395         chap_compute_md5(rchap->rchap_id, rchap->rchap_secret,
396             rchap->rchap_challenge, rchap->rchap_challenge_len,
397             response_bin, response_bin_len);
398
399         *responsep = response_bin;
400         *response_lenp = response_bin_len;
401 }
402
403 char *
404 rchap_get_response(struct rchap *rchap)
405 {
406         void *response;
407         size_t response_len;
408         char *chap_r;
409
410         rchap_get_response_bin(rchap, &response, &response_len);
411         chap_r = chap_bin2hex(response, response_len);
412         free(response);
413
414         return (chap_r);
415 }
416
417 void
418 rchap_delete(struct rchap *rchap)
419 {
420
421         free(rchap->rchap_secret);
422         free(rchap->rchap_challenge);
423         free(rchap);
424 }