source: openpam/trunk/lib/liboath/oath_key.c @ 679

Last change on this file since 679 was 679, checked in by Dag-Erling Smørgrav, 8 years ago

Clean up and simplify dummy key handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 7.5 KB
Line 
1/*-
2 * Copyright (c) 2013 Universitetet i Oslo
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote
14 *    products derived from this software without specific prior written
15 *    permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Id: oath_key.c 679 2013-03-18 21:38:58Z des $
30 */
31
32#ifdef HAVE_CONFIG_H
33# include "config.h"
34#endif
35
36#include <sys/types.h>
37
38#include <errno.h>
39#include <inttypes.h>
40#include <limits.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45
46#include <security/pam_appl.h>
47#include <security/openpam.h>
48
49#include "openpam_asprintf.h"
50#include "openpam_strlcmp.h"
51
52#include <security/oath.h>
53
54/*
55 * Allocate a struct oath_key with sufficient additional space for the
56 * label and key.
57 */
58struct oath_key *
59oath_key_alloc(void)
60{
61        struct oath_key *key;
62
63        if ((key = calloc(1, sizeof *key)) == NULL) {
64                openpam_log(PAM_LOG_ERROR, "malloc(): %s", strerror(errno));
65                return (NULL);
66        }
67        /* XXX should try to wire */
68        return (key);
69}
70
71/*
72 * Wipe and free a struct oath_key
73 */
74void
75oath_key_free(struct oath_key *key)
76{
77
78        if (key != NULL) {
79                memset(key, 0, sizeof *key);
80                free(key);
81        }
82}
83
84/*
85 * Allocate a struct oath_key and populate it from a Google Authenticator
86 * otpauth URI
87 */
88struct oath_key *
89oath_key_from_uri(const char *uri)
90{
91        struct oath_key *key;
92        const char *p, *q, *r;
93        uintmax_t n;
94        char *e;
95
96        if ((key = oath_key_alloc()) == NULL)
97                return (NULL);
98
99        /* check method */
100        p = uri;
101        if (strlcmp("otpauth://", p, 10) != 0)
102                goto invalid;
103        p += 10;
104
105        /* check mode (hotp = event, totp = time-sync) */
106        if ((q = strchr(p, '/')) == NULL)
107                goto invalid;
108        if (strlcmp("hotp", p, q - p) == 0) {
109                key->mode = om_hotp;
110        } else if (strlcmp("totp", p, q - p) == 0) {
111                key->mode = om_totp;
112        } else {
113                goto invalid;
114        }
115        p = q + 1;
116
117        /* extract label */
118        if ((q = strchr(p, '?')) == NULL)
119                goto invalid;
120        if ((key->labellen = q - p + 1) > sizeof key->label)
121                goto invalid;
122        memcpy(key->label, p, q - p);
123        key->label[q - p] = '\0';
124        p = q + 1;
125
126        /* extract parameters */
127        key->counter = UINT64_MAX;
128        while (*p != '\0') {
129                if ((q = strchr(p, '=')) == NULL)
130                        goto invalid;
131                q = q + 1;
132                if ((r = strchr(p, '&')) == NULL)
133                        r = strchr(p, '\0');
134                if (r < q)
135                        /* & before = */
136                        goto invalid;
137                /* p points to key, q points to value, r points to & or NUL */
138                if (strlcmp("secret=", p, q - p) == 0) {
139                        if (key->keylen != 0)
140                                /* dupe */
141                                goto invalid;
142                        key->keylen = sizeof key->key;
143                        if (base32_dec(q, r - q, key->key, &key->keylen) != 0)
144                                goto invalid;
145                        if (base32_enclen(key->keylen) != (size_t)(r - q))
146                                goto invalid;
147                } else if (strlcmp("algorithm=", p, q - p) == 0) {
148                        if (key->hash != oh_undef)
149                                /* dupe */
150                                goto invalid;
151                        if (strlcmp("SHA1", q, r - q) == 0)
152                                key->hash = oh_sha1;
153                        else if (strlcmp("SHA256", q, r - q) == 0)
154                                key->hash = oh_sha256;
155                        else if (strlcmp("SHA512", q, r - q) == 0)
156                                key->hash = oh_sha512;
157                        else if (strlcmp("MD5", q, r - q) == 0)
158                                key->hash = oh_md5;
159                        else
160                                goto invalid;
161                } else if (strlcmp("digits=", p, q - p) == 0) {
162                        if (key->digits != 0)
163                                /* dupe */
164                                goto invalid;
165                        /* only 6 or 8 */
166                        if (r - q != 1 || (*q != '6' && *q != '8'))
167                                goto invalid;
168                        key->digits = *q - '0';
169                } else if (strlcmp("counter=", p, q - p) == 0) {
170                        if (key->counter != UINT64_MAX)
171                                /* dupe */
172                                goto invalid;
173                        n = strtoumax(q, &e, 10);
174                        if (e != r || n >= UINT64_MAX)
175                                goto invalid;
176                        key->counter = (uint64_t)n;
177                } else if (strlcmp("period=", p, q - p) == 0) {
178                        if (key->timestep != 0)
179                                /* dupe */
180                                goto invalid;
181                        n = strtoumax(q, &e, 10);
182                        if (e != r || n > OATH_MAX_TIMESTEP)
183                                goto invalid;
184                        key->timestep = n;
185                } else {
186                        goto invalid;
187                }
188                /* final parameter? */
189                if (*r == '\0')
190                        break;
191                /* skip & and continue */
192                p = r + 1;
193        }
194
195        /* sanity checks and default values */
196        if (key->mode == om_hotp) {
197                if (key->timestep != 0)
198                        goto invalid;
199                if (key->counter == UINTMAX_MAX)
200                        key->counter = 0;
201        } else if (key->mode == om_totp) {
202                if (key->counter != UINTMAX_MAX)
203                        goto invalid;
204                if (key->timestep == 0)
205                        key->timestep = OATH_DEF_TIMESTEP;
206        } else {
207                /* unreachable */
208                oath_key_free(key);
209                return (NULL);
210        }
211        if (key->hash == oh_undef)
212                key->hash = oh_sha1;
213        if (key->digits == 0)
214                key->digits = 6;
215        if (key->keylen == 0)
216                goto invalid;
217        return (key);
218
219invalid:
220        openpam_log(PAM_LOG_NOTICE, "invalid OATH URI: %s", uri);
221        oath_key_free(key);
222        return (NULL);
223}
224
225struct oath_key *
226oath_key_from_file(const char *filename)
227{
228        struct oath_key *key;
229        FILE *f;
230        char *line;
231        size_t len;
232
233        if ((f = fopen(filename, "r")) == NULL)
234                return (NULL);
235        /* get first non-empty non-comment line */
236        line = openpam_readline(f, NULL, &len);
237        if (strlcmp("otpauth://", line, len) == 0) {
238                key = oath_key_from_uri(line);
239        } else {
240                openpam_log(PAM_LOG_ERROR,
241                    "unrecognized key file format: %s", filename);
242                key = NULL;
243        }
244        fclose(f);
245        return (key);
246}
247
248char *
249oath_key_to_uri(const struct oath_key *key)
250{
251        const char *hash;
252        char *tmp, *uri;
253        size_t kslen, urilen;
254
255        switch (key->hash) {
256        case oh_sha1:
257                hash = "SHA1";
258                break;
259        case oh_sha256:
260                hash = "SHA256";
261                break;
262        case oh_sha512:
263                hash = "SHA512";
264                break;
265        case oh_md5:
266                hash = "MD5";
267                break;
268        default:
269                return (NULL);
270        }
271
272        if (key->mode == om_hotp) {
273                urilen = asprintf(&uri, "otpauth://"
274                    "%s/%s?algorithm=%s&digits=%d&counter=%ju&secret=",
275                    "hotp", key->label, hash, key->digits,
276                    (uintmax_t)key->counter);
277        } else if (key->mode == om_totp) {
278                urilen = asprintf(&uri, "otpauth://"
279                    "%s/%s?algorithm=%s&digits=%d&period=%u&secret=",
280                    "totp", key->label, hash, key->digits, key->timestep);
281        } else {
282                /* unreachable */
283                return (NULL);
284        }
285
286        /* compute length of base32-encoded key and append it */
287        kslen = base32_enclen(key->keylen) + 1;
288        if ((tmp = realloc(uri, urilen + kslen)) == NULL) {
289                free(uri);
290                return (NULL);
291        }
292        uri = tmp;
293        if (base32_enc(key->key, key->keylen, uri + urilen, &kslen) != 0) {
294                free(uri);
295                return (NULL);
296        }
297
298        return (uri);
299}
300
301struct oath_key *
302oath_dummy_key(enum oath_mode mode, enum oath_hash hash, unsigned int digits)
303{
304        struct oath_key *key;
305
306        if ((key = oath_key_alloc()) == NULL)
307                return (NULL);
308        key->mode = mode;
309        key->digits = digits;
310        key->counter = 0;
311        key->timestep = 30;
312        key->hash = hash;
313        strcpy(key->label, "oath-dummy-key");
314        key->labellen = strlen(key->label);
315        key->keylen = sizeof key->key;
316        return (key);
317}
Note: See TracBrowser for help on using the repository browser.