source: openpam/trunk/bin/oathkey/oathkey.c @ 827

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

Add a calc command that prints the current code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.4 KB
Line 
1/*-
2 * Copyright (c) 2013-2014 The University of 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: oathkey.c 827 2014-10-22 10:03:14Z des $
30 */
31
32#ifdef HAVE_CONFIG_H
33# include "config.h"
34#endif
35
36#include <sys/types.h>
37
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <limits.h>
42#include <pwd.h>
43#include <stdint.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49#include "openpam_asprintf.h"
50
51#include <security/oath.h>
52
53enum { RET_SUCCESS, RET_FAILURE, RET_ERROR, RET_USAGE, RET_UNAUTH };
54
55static char *user;
56static char *keyfile;
57static int verbose;
58static int writeback;
59
60static int isroot;              /* running as root */
61static int issameuser;          /* real user same as target user */
62
63/*
64 * Print key in hexadecimal form
65 */
66static int
67oathkey_print_hex(struct oath_key *key)
68{
69        unsigned int i;
70
71        for (i = 0; i < key->keylen; ++i)
72                printf("%02x", key->key[i]);
73        printf("\n");
74        return (RET_SUCCESS);
75}
76
77/*
78 * Print key in otpauth URI form
79 */
80static int
81oathkey_print_uri(struct oath_key *key)
82{
83        char *keyuri;
84
85        if ((keyuri = oath_key_to_uri(key)) == NULL) {
86                warnx("failed to convert key to otpauth URI");
87                return (RET_ERROR);
88        }
89        printf("%s\n", keyuri);
90        free(keyuri);
91        return (RET_SUCCESS);
92}
93
94/*
95 * Load key from file
96 */
97static int
98oathkey_load(struct oath_key **key)
99{
100
101        if (verbose)
102                warnx("loading key from %s", keyfile);
103        if ((*key = oath_key_from_file(keyfile)) == NULL) {
104                warn("%s", keyfile);
105                if (errno == EACCES || errno == EPERM)
106                        return (RET_UNAUTH);
107                return (RET_ERROR);
108        }
109        return (RET_SUCCESS);
110}
111
112/*
113 * Save key to file
114 * XXX liboath should take care of this for us
115 */
116static int
117oathkey_save(struct oath_key *key)
118{
119        char *keyuri;
120        int fd, len, ret;
121
122        if (verbose)
123                warnx("saving key to %s", keyfile);
124        keyuri = NULL;
125        len = 0;
126        fd = ret = -1;
127        if ((keyuri = oath_key_to_uri(key)) == NULL) {
128                warnx("failed to convert key to otpauth URI");
129                goto done;
130        }
131        len = strlen(keyuri);
132        if ((fd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0 ||
133            write(fd, keyuri, len) != len || write(fd, "\n", 1) != 1) {
134                warn("%s", keyfile);
135                goto done;
136        }
137        ret = 0;
138done:
139        if (fd >= 0)
140                close(fd);
141        if (keyuri != NULL)
142                free(keyuri);
143        return (ret);
144}
145
146/*
147 * Generate a new key
148 */
149static int
150oathkey_genkey(int argc, char *argv[])
151{
152        struct oath_key *key;
153        int ret;
154
155        /* XXX add parameters later */
156        if (argc != 0)
157                return (RET_USAGE);
158        (void)argv;
159        if (!isroot && !issameuser)
160                return (RET_UNAUTH);
161        if ((key = oath_key_create(user, om_totp, oh_undef, NULL, 0)) == NULL)
162                return (RET_ERROR);
163        ret = writeback ? oathkey_save(key) : oathkey_print_uri(key);
164        oath_key_free(key);
165        return (ret);
166}
167
168/*
169 * Set a user's key
170 */
171static int
172oathkey_setkey(int argc, char *argv[])
173{
174        struct oath_key *key;
175        int ret;
176
177        /* XXX add parameters later */
178        if (argc != 1)
179                return (RET_USAGE);
180        (void)argv;
181        if (!isroot && !issameuser)
182                return (RET_UNAUTH);
183        if ((key = oath_key_from_uri(argv[0])) == NULL)
184                return (RET_ERROR);
185        ret = oathkey_save(key);
186        oath_key_free(key);
187        return (ret);
188}
189
190/*
191 * Print raw key in hexadecimal
192 */
193static int
194oathkey_getkey(int argc, char *argv[])
195{
196        struct oath_key *key;
197        int ret;
198
199        if (argc != 0)
200                return (RET_USAGE);
201        (void)argv;
202        if (!isroot && !issameuser)
203                return (RET_UNAUTH);
204        if ((ret = oathkey_load(&key)) != RET_SUCCESS)
205                return (ret);
206        ret = oathkey_print_hex(key);
207        oath_key_free(key);
208        return (ret);
209}
210
211/*
212 * Print the otpauth URI for a key
213 */
214static int
215oathkey_geturi(int argc, char *argv[])
216{
217        struct oath_key *key;
218        int ret;
219
220        if (argc != 0)
221                return (RET_USAGE);
222        (void)argv;
223        if (!isroot && !issameuser)
224                return (RET_UNAUTH);
225        if ((ret = oathkey_load(&key)) != RET_SUCCESS)
226                return (ret);
227        ret = oathkey_print_uri(key);
228        oath_key_free(key);
229        return (ret);
230}
231
232/*
233 * Check whether a given response is correct for the given keyfile.
234 */
235static int
236oathkey_verify(int argc, char *argv[])
237{
238        struct oath_key *key;
239        unsigned long response;
240        char *end;
241        int match, ret;
242
243        if (argc < 1)
244                return (RET_USAGE);
245        if ((ret = oathkey_load(&key)) != RET_SUCCESS)
246                return (ret);
247        response = strtoul(*argv, &end, 10);
248        if (end == *argv || *end != '\0')
249                response = ULONG_MAX; /* never valid */
250        if (key->mode == om_totp)
251                match = oath_totp_match(key, response, 3 /* XXX window */);
252        else if (key->mode == om_hotp)
253                match = oath_hotp_match(key, response, 17 /* XXX window */);
254        else
255                match = -1;
256        /* oath_*_match() return -1 on error, 0 on failure, 1 on success */
257        if (match < 0) {
258                warnx("OATH error");
259                match = 0;
260        }
261        if (verbose)
262                warnx("response: %lu %s", response,
263                    match ? "matched" : "did not match");
264        ret = match ? RET_SUCCESS : RET_FAILURE;
265        if (match && writeback)
266                ret = oathkey_save(key);
267        oath_key_free(key);
268        return (ret);
269}
270
271/*
272 * Compute the current code
273 */
274static int
275oathkey_calc(int argc, char *argv[])
276{
277        struct oath_key *key;
278        unsigned int current;
279        int ret;
280
281        if (argc != 0)
282                return (RET_USAGE);
283        (void)argv;
284        if ((ret = oathkey_load(&key)) != RET_SUCCESS)
285                return (ret);
286        if (key->mode == om_totp)
287                current = oath_totp_current(key);
288        else if (key->mode == om_hotp)
289                current = oath_hotp_current(key);
290        else
291                current = -1;
292        if (current == (unsigned int)-1) {
293                warnx("OATH error");
294                ret = RET_ERROR;
295        } else {
296                printf("%.*d\n", (int)key->digits, current);
297                ret = RET_SUCCESS;
298                if (writeback)
299                        ret = oathkey_save(key);
300        }
301        oath_key_free(key);
302        return (ret);
303}
304
305/*
306 * Print usage string and exit.
307 */
308static void
309usage(void)
310{
311        fprintf(stderr,
312            "usage: oathkey [-hvw] [-u user] [-k keyfile] <command>\n"
313            "\n"
314            "Commands:\n"
315            "    calc        Print the current code\n"
316            "    genkey      Generate a new key\n"
317            "    getkey      Print the key in hexadecimal form\n"
318            "    geturi      Print the key in otpauth URI form\n"
319            "    setkey      Generate a new key\n"
320            "    verify <response>\n"
321            "                Verify a response\n");
322        exit(1);
323}
324
325int
326main(int argc, char *argv[])
327{
328        struct passwd *pw;
329        int opt, ret;
330        char *cmd;
331
332        /*
333         * Parse command-line options
334         */
335        while ((opt = getopt(argc, argv, "hk:u:vw")) != -1)
336                switch (opt) {
337                case 'k':
338                        keyfile = optarg;
339                        break;
340                case 'u':
341                        user = optarg;
342                        break;
343                case 'v':
344                        ++verbose;
345                        break;
346                case 'w':
347                        ++writeback;
348                        break;
349                case 'h':
350                default:
351                        usage();
352                }
353
354        argc -= optind;
355        argv += optind;
356
357        if (argc-- < 1)
358                usage();
359        cmd = *argv++;
360
361        /*
362         * Check whether we are (really!) root.
363         */
364        if (getuid() == 0)
365                isroot = 1;
366
367        /*
368         * If a user was specified on the command line, check whether it
369         * matches our real UID.
370         */
371        if (user != NULL) {
372                if ((pw = getpwnam(user)) == NULL)
373                        errx(1, "no such user");
374                if (getuid() == pw->pw_uid)
375                        issameuser = 1;
376        }
377
378        /*
379         * If no user was specified on the command line, look up the user
380         * that corresponds to our real UID.
381         */
382        if (user == NULL) {
383                if ((pw = getpwuid(getuid())) == NULL)
384                        errx(1, "who are you?");
385                if (asprintf(&user, "%s", pw->pw_name) < 0)
386                        err(1, "asprintf()");
387                issameuser = 1;
388        }
389
390        /*
391         * If no keyfile was specified on the command line, derive it from
392         * the user name.
393         */
394        if (keyfile == NULL)
395                /* XXX replace with a function that searches multiple locations? */
396                if (asprintf(&keyfile, "/var/oath/%s.otpauth", user) < 0)
397                        err(1, "asprintf()");
398
399        /*
400         * Execute the requested command
401         */
402        if (strcmp(cmd, "help") == 0)
403                ret = RET_USAGE;
404        else if (strcmp(cmd, "calc") == 0)
405                ret = oathkey_calc(argc, argv);
406        else if (strcmp(cmd, "genkey") == 0)
407                ret = oathkey_genkey(argc, argv);
408        else if (strcmp(cmd, "getkey") == 0)
409                ret = oathkey_getkey(argc, argv);
410        else if (strcmp(cmd, "geturi") == 0 || strcmp(cmd, "uri") == 0)
411                ret = oathkey_geturi(argc, argv);
412        else if (strcmp(cmd, "setkey") == 0)
413                ret = oathkey_setkey(argc, argv);
414        else if (strcmp(cmd, "verify") == 0)
415                ret = oathkey_verify(argc, argv);
416        else
417                ret = RET_USAGE;
418
419        /*
420         * Check result and act accordingly
421         */
422        switch (ret) {
423        case RET_UNAUTH:
424                errno = EPERM;
425                err(1, "%s", cmd);
426                break;
427        case RET_USAGE:
428                usage();
429                break;
430        case RET_SUCCESS:
431                exit(0);
432                break;
433        case RET_FAILURE:
434                exit(1);
435                break;
436        case RET_ERROR:
437                exit(2);
438                break;
439        default:
440                exit(3);
441                break;
442        }
443        /* not reached */
444        exit(255);
445}
Note: See TracBrowser for help on using the repository browser.