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

Last change on this file since 818 was 818, checked in by Dag-Erling Smørgrav, 7 years ago
  • Set the sameuser flag when a non-root user manipulates their own key.
  • Rename the uri command to geturi (but retain backward compatibility).
  • Add a getkey command that prints the key in hexadecimal.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.5 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 818 2014-10-08 11:02:44Z 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 * Save key to file
96 * XXX liboath should take care of this for us
97 */
98static int
99oathkey_save(struct oath_key *key)
100{
101        char *keyuri;
102        int fd, len, ret;
103
104        if (verbose)
105                warnx("saving key to %s", keyfile);
106        keyuri = NULL;
107        len = 0;
108        fd = ret = -1;
109        if ((keyuri = oath_key_to_uri(key)) == NULL) {
110                warnx("failed to convert key to otpauth URI");
111                goto done;
112        }
113        len = strlen(keyuri);
114        if ((fd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0 ||
115            write(fd, keyuri, len) != len || write(fd, "\n", 1) != 1) {
116                warn("%s", keyfile);
117                goto done;
118        }
119        ret = 0;
120done:
121        if (fd >= 0)
122                close(fd);
123        if (keyuri != NULL)
124                free(keyuri);
125        return (ret);
126}
127
128/*
129 * Generate a new key
130 */
131static int
132oathkey_genkey(int argc, char *argv[])
133{
134        struct oath_key *key;
135        int ret;
136
137        /* XXX add parameters later */
138        if (argc != 0)
139                return (RET_USAGE);
140        (void)argv;
141        if (!isroot && !issameuser)
142                return (RET_UNAUTH);
143        if ((key = oath_key_create(user, om_totp, oh_undef, NULL, 0)) == NULL)
144                return (RET_ERROR);
145        ret = writeback ? oathkey_save(key) : oathkey_print_uri(key);
146        oath_key_free(key);
147        return (ret);
148}
149
150/*
151 * Set a user's key
152 */
153static int
154oathkey_setkey(int argc, char *argv[])
155{
156        struct oath_key *key;
157        int ret;
158
159        /* XXX add parameters later */
160        if (argc != 1)
161                return (RET_USAGE);
162        (void)argv;
163        if (!isroot && !issameuser)
164                return (RET_UNAUTH);
165        if ((key = oath_key_from_uri(argv[0])) == NULL)
166                return (RET_ERROR);
167        ret = oathkey_save(key);
168        oath_key_free(key);
169        return (ret);
170}
171
172/*
173 * Print raw key in hexadecimal
174 */
175static int
176oathkey_getkey(int argc, char *argv[])
177{
178        struct oath_key *key;
179        int ret;
180
181        if (argc != 0)
182                return (RET_USAGE);
183        (void)argv;
184        if (!isroot && !issameuser)
185                return (RET_UNAUTH);
186        if (verbose)
187                warnx("loading key from %s", keyfile);
188        if ((key = oath_key_from_file(keyfile)) == NULL)
189                return (RET_ERROR);
190        ret = oathkey_print_hex(key);
191        oath_key_free(key);
192        return (ret);
193}
194
195/*
196 * Print the otpauth URI for a key
197 */
198static int
199oathkey_geturi(int argc, char *argv[])
200{
201        struct oath_key *key;
202        int ret;
203
204        if (argc != 0)
205                return (RET_USAGE);
206        (void)argv;
207        if (!isroot && !issameuser)
208                return (RET_UNAUTH);
209        if (verbose)
210                warnx("loading key from %s", keyfile);
211        if ((key = oath_key_from_file(keyfile)) == NULL)
212                return (RET_ERROR);
213        ret = oathkey_print_uri(key);
214        oath_key_free(key);
215        return (ret);
216}
217
218/*
219 * Check whether a given response is correct for the given keyfile.
220 */
221static int
222oathkey_verify(int argc, char *argv[])
223{
224        struct oath_key *key;
225        unsigned long response;
226        char *end;
227        int match, ret;
228
229        if (argc < 1)
230                return (RET_USAGE);
231        if (verbose)
232                warnx("loading key from %s", keyfile);
233        if ((key = oath_key_from_file(keyfile)) == NULL)
234                return (RET_ERROR);
235        response = strtoul(*argv, &end, 10);
236        if (end == *argv || *end != '\0')
237                response = ULONG_MAX; /* never valid */
238        if (key->mode == om_totp)
239                match = oath_totp_match(key, response, 3 /* XXX window */);
240        else if (key->mode == om_hotp)
241                match = oath_hotp_match(key, response, 17 /* XXX window */);
242        else
243                match = -1;
244        /* oath_*_match() return -1 on error, 0 on failure, 1 on success */
245        if (match < 0) {
246                warnx("OATH error");
247                match = 0;
248        }
249        if (verbose)
250                warnx("response: %lu %s", response,
251                    match ? "matched" : "did not match");
252        ret = match ? RET_SUCCESS : RET_FAILURE;
253        if (match && writeback)
254                ret = oathkey_save(key);
255        oath_key_free(key);
256        return (ret);
257}
258
259/*
260 * Print usage string and exit.
261 */
262static void
263usage(void)
264{
265        fprintf(stderr,
266            "usage: oathkey [-hvw] [-u user] [-k keyfile] <command>\n"
267            "\n"
268            "Commands:\n"
269            "    genkey      Generate a new key\n"
270            "    getkey      Print the key in hexadecimal form\n"
271            "    geturi      Print the key in otpauth URI form\n"
272            "    setkey      Generate a new key\n"
273            "    verify <response>\n"
274            "                Verify a response\n");
275        exit(1);
276}
277
278int
279main(int argc, char *argv[])
280{
281        struct passwd *pw;
282        int opt, ret;
283        char *cmd;
284
285        /*
286         * Parse command-line options
287         */
288        while ((opt = getopt(argc, argv, "hk:u:vw")) != -1)
289                switch (opt) {
290                case 'k':
291                        keyfile = optarg;
292                        break;
293                case 'u':
294                        user = optarg;
295                        break;
296                case 'v':
297                        ++verbose;
298                        break;
299                case 'w':
300                        ++writeback;
301                        break;
302                case 'h':
303                default:
304                        usage();
305                }
306
307        argc -= optind;
308        argv += optind;
309
310        if (argc-- < 1)
311                usage();
312        cmd = *argv++;
313
314        /*
315         * Check whether we are (really!) root.
316         */
317        if (getuid() == 0)
318                isroot = 1;
319
320        /*
321         * If a user was specified on the command line, check whether it
322         * matches our real UID.
323         */
324        if (user != NULL) {
325                if ((pw = getpwnam(user)) == NULL)
326                        errx(1, "no such user");
327                if (getuid() == pw->pw_uid)
328                        issameuser = 1;
329        }
330
331        /*
332         * If no user was specified on the command line, look up the user
333         * that corresponds to our real UID.
334         */
335        if (user == NULL) {
336                if ((pw = getpwuid(getuid())) == NULL)
337                        errx(1, "who are you?");
338                if (asprintf(&user, "%s", pw->pw_name) < 0)
339                        err(1, "asprintf()");
340                issameuser = 1;
341        }
342
343        /*
344         * If no keyfile was specified on the command line, derive it from
345         * the user name.
346         */
347        if (keyfile == NULL)
348                /* XXX replace with a function that searches multiple locations? */
349                if (asprintf(&keyfile, "/var/oath/%s.otpauth", user) < 0)
350                        err(1, "asprintf()");
351
352        /*
353         * Execute the requested command
354         */
355        if (strcmp(cmd, "help") == 0)
356                ret = RET_USAGE;
357        else if (strcmp(cmd, "genkey") == 0)
358                ret = oathkey_genkey(argc, argv);
359        else if (strcmp(cmd, "getkey") == 0)
360                ret = oathkey_getkey(argc, argv);       
361        else if (strcmp(cmd, "geturi") == 0 || strcmp(cmd, "uri") == 0)
362                ret = oathkey_geturi(argc, argv);
363        else if (strcmp(cmd, "setkey") == 0)
364                ret = oathkey_setkey(argc, argv);
365        else if (strcmp(cmd, "verify") == 0)
366                ret = oathkey_verify(argc, argv);
367        else
368                ret = RET_USAGE;
369
370        /*
371         * Check result and act accordingly
372         */
373        switch (ret) {
374        case RET_UNAUTH:
375                errno = EPERM;
376                err(1, "%s", cmd);
377                break;
378        case RET_USAGE:
379                usage();
380                break;
381        case RET_SUCCESS:
382                exit(0);
383                break;
384        case RET_FAILURE:
385                exit(1);
386                break;
387        case RET_ERROR:
388                exit(2);
389                break;
390        default:
391                exit(3);
392                break;
393        }
394        /* not reached */
395        exit(255);
396}
Note: See TracBrowser for help on using the repository browser.