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

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

Implement keyfile writeback.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 7.7 KB
Line 
1/*-
2 * Copyright (c) 2013-2014 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: oathkey.c 784 2014-03-10 15:31:30Z 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 otpauth URI form
65 */
66static int
67oathkey_print(struct oath_key *key)
68{
69        char *keyuri;
70
71        if ((keyuri = oath_key_to_uri(key)) == NULL) {
72                warnx("failed to convert key to otpauth URI");
73                return (RET_ERROR);
74        }
75        printf("%s\n", keyuri);
76        free(keyuri);
77        return (RET_SUCCESS);
78}
79
80/*
81 * Save key to file
82 * XXX liboath should take care of this for us
83 */
84static int
85oathkey_save(struct oath_key *key)
86{
87        char *keyuri;
88        int fd, len, ret;
89
90        keyuri = NULL;
91        len = 0;
92        fd = ret = -1;
93        if ((keyuri = oath_key_to_uri(key)) == NULL) {
94                warnx("failed to convert key to otpauth URI");
95                goto done;
96        }
97        len = strlen(keyuri);
98        if ((fd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0 ||
99            write(fd, keyuri, len) != len || write(fd, "\n", 1) != 1) {
100                warn("%s", keyfile);
101                goto done;
102        }
103        ret = 0;
104done:
105        if (fd >= 0)
106                close(fd);
107        if (keyuri != NULL)
108                free(keyuri);
109        return (ret);
110}
111
112/*
113 * Generate a new key
114 */
115static int
116oathkey_genkey(int argc, char *argv[])
117{
118        struct oath_key *key;
119        int ret;
120
121        /* XXX add parameters later */
122        if (argc != 0)
123                return (RET_USAGE);
124        (void)argv;
125
126        /* don't let users generate keys for eachother */
127        if (!isroot && !issameuser)
128                return (RET_UNAUTH);
129
130        if ((key = oath_key_create(user, om_totp, oh_undef, NULL, 0)) == NULL)
131                return (RET_ERROR);
132        ret = writeback ? oathkey_save(key) : oathkey_print(key);
133        oath_key_free(key);
134        return (ret);
135}
136
137/*
138 * Set a user's key
139 */
140static int
141oathkey_setkey(int argc, char *argv[])
142{
143        struct oath_key *key;
144        int ret;
145
146        /* XXX add parameters later */
147        if (argc != 1)
148                return (RET_USAGE);
149        (void)argv;
150
151        /* don't let users set eachother's keys */
152        if (!isroot && !issameuser)
153                return (RET_UNAUTH);
154
155        if ((key = oath_key_from_uri(argv[0])) == NULL)
156                return (RET_ERROR);
157        ret = oathkey_save(key);
158        oath_key_free(key);
159        return (ret);
160}
161
162/*
163 * Print the otpauth URI for a key
164 */
165static int
166oathkey_uri(int argc, char *argv[])
167{
168        struct oath_key *key;
169        int ret;
170
171        if (argc != 0)
172                return (RET_USAGE);
173        (void)argv;
174
175        /* don't let users see eachother's keys */
176        if (!isroot && !issameuser)
177                return (RET_UNAUTH);
178
179        if ((key = oath_key_from_file(keyfile)) == NULL)
180                return (RET_ERROR);
181        ret = oathkey_print(key);
182        oath_key_free(key);
183        return (ret);
184}
185
186/*
187 * Check whether a given response is correct for the given keyfile.
188 */
189static int
190oathkey_verify(int argc, char *argv[])
191{
192        struct oath_key *key;
193        unsigned long response;
194        char *end;
195        int match, ret;
196
197        if (argc < 1)
198                return (RET_USAGE);
199        if ((key = oath_key_from_file(keyfile)) == NULL)
200                return (RET_ERROR);
201        response = strtoul(*argv, &end, 10);
202        if (end == *argv || *end != '\0')
203                response = ULONG_MAX; /* never valid */
204        if (key->mode == om_totp)
205                match = oath_totp_match(key, response, 3 /* XXX window */);
206        else if (key->mode == om_hotp)
207                match = oath_hotp_match(key, response, 17 /* XXX window */);
208        else
209                match = -1;
210        /* oath_*_match() return -1 on error, 0 on failure, 1 on success */
211        if (match < 0) {
212                warnx("OATH error");
213                match = 0;
214        }
215        if (verbose)
216                warnx("response: %lu %s", response,
217                    match ? "matched" : "did not match");
218        ret = match ? RET_SUCCESS : RET_FAILURE;
219        if (match && writeback)
220                ret = oathkey_save(key);
221        oath_key_free(key);
222        return (ret);
223}
224
225/*
226 * Print usage string and exit.
227 */
228static void
229usage(void)
230{
231        fprintf(stderr,
232            "usage: oathkey [-hvw] [-u user] [-k keyfile] <command>\n"
233            "\n"
234            "Commands:\n"
235            "    genkey      Generate a new key\n"
236            "    setkey      Generate a new key\n"
237            "    uri         Print the key in otpauth URI form\n"
238            "    verify <response>\n"
239            "                Verify a response\n");
240        exit(1);
241}
242
243int
244main(int argc, char *argv[])
245{
246        struct passwd *pw;
247        int opt, ret;
248        char *cmd;
249
250        /*
251         * Parse command-line options
252         */
253        while ((opt = getopt(argc, argv, "hk:u:vw")) != -1)
254                switch (opt) {
255                case 'k':
256                        keyfile = optarg;
257                        break;
258                case 'u':
259                        user = optarg;
260                        break;
261                case 'v':
262                        ++verbose;
263                        break;
264                case 'w':
265                        ++writeback;
266                        break;
267                case 'h':
268                default:
269                        usage();
270                }
271
272        argc -= optind;
273        argv += optind;
274
275        if (argc-- < 1)
276                usage();
277        cmd = *argv++;
278
279        /*
280         * Check whether we are (really!) root.
281         */
282        if (getuid() == 0)
283                isroot = 1;
284
285        /*
286         * If a user was specified on the command line, check whether it
287         * matches our real UID.
288         */
289        if (user != NULL) {
290                if ((pw = getpwnam(user)) == NULL)
291                        errx(1, "no such user");
292                if (getuid() == pw->pw_uid)
293                        issameuser = 1;
294        }
295
296        /*
297         * If no user was specified on the command line, look up the user
298         * that corresponds to our real UID.
299         */
300        if (user == NULL) {
301                if ((pw = getpwuid(getuid())) == NULL)
302                        errx(1, "who are you?");
303                if (asprintf(&user, "%s", pw->pw_name) < 0)
304                        err(1, "asprintf()");
305        }
306
307        /*
308         * If no keyfile was specified on the command line, derive it from
309         * the user name.
310         */
311        if (keyfile == NULL)
312                /* XXX replace with a function that searches multiple locations? */
313                if (asprintf(&keyfile, "/var/oath/%s.otpauth", user) < 0)
314                        err(1, "asprintf()");
315
316        /*
317         * Execute the requested command
318         */
319        if (strcmp(cmd, "help") == 0)
320                ret = RET_USAGE;
321        else if (strcmp(cmd, "genkey") == 0)
322                ret = oathkey_genkey(argc, argv);
323        else if (strcmp(cmd, "setkey") == 0)
324                ret = oathkey_setkey(argc, argv);
325        else if (strcmp(cmd, "uri") == 0)
326                ret = oathkey_uri(argc, argv);
327        else if (strcmp(cmd, "verify") == 0)
328                ret = oathkey_verify(argc, argv);
329        else
330                ret = RET_USAGE;
331
332        /*
333         * Check result and act accordingly
334         */
335        switch (ret) {
336        case RET_UNAUTH:
337                errno = EPERM;
338                err(1, "%s", cmd);
339                break;
340        case RET_USAGE:
341                usage();
342                break;
343        case RET_SUCCESS:
344                exit(0);
345                break;
346        case RET_FAILURE:
347                exit(1);
348                break;
349        case RET_ERROR:
350                exit(2);
351                break;
352        default:
353                exit(3);
354                break;
355        }
356        /* not reached */
357        exit(255);
358}
Note: See TracBrowser for help on using the repository browser.