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

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

Rudimentary key management tool.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 6.8 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 780 2014-03-10 09:55:15Z 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 <limits.h>
41#include <pwd.h>
42#include <stdint.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48#include "openpam_asprintf.h"
49
50#include <security/oath.h>
51
52enum { RET_SUCCESS, RET_FAILURE, RET_ERROR, RET_USAGE, RET_UNAUTH };
53
54static char *user;
55static char *keyfile;
56static int verbose;
57
58static int isroot;              /* running as root */
59static int issameuser;          /* real user same as target user */
60
61/*
62 * Generate a new key
63 */
64static int
65oathkey_genkey(int argc, char *argv[])
66{
67        struct oath_key *key;
68
69        /* XXX add parameters later */
70        if (argc != 0)
71                return (RET_USAGE);
72        (void)argv;
73
74        /* don't let users generate keys for eachother */
75        if (!isroot && !issameuser)
76                return (RET_UNAUTH);
77
78        if ((key = oath_key_create(user, om_totp, oh_undef, NULL, 0)) == NULL)
79                return (RET_ERROR);
80        /* XXX should save to file, not print */
81        printf("%s\n", oath_key_to_uri(key));
82        oath_key_free(key);
83        return (RET_SUCCESS);
84}
85
86/*
87 * Set a user's key
88 */
89static int
90oathkey_setkey(int argc, char *argv[])
91{
92        struct oath_key *key;
93
94        /* XXX add parameters later */
95        if (argc != 1)
96                return (RET_USAGE);
97        (void)argv;
98
99        /* don't let users set eachother's keys */
100        if (!isroot && !issameuser)
101                return (RET_UNAUTH);
102
103        if ((key = oath_key_from_uri(argv[0])) == NULL)
104                return (RET_ERROR);
105        /* XXX should save to file, not print */
106        printf("%s\n", oath_key_to_uri(key));
107        oath_key_free(key);
108        return (RET_SUCCESS);
109}
110
111/*
112 * Print the otpauth URI for a key
113 */
114static int
115oathkey_uri(int argc, char *argv[])
116{
117        struct oath_key *key;
118
119        if (argc != 0)
120                return (RET_USAGE);
121        (void)argv;
122
123        /* don't let users see eachother's keys */
124        if (!isroot && !issameuser)
125                return (RET_UNAUTH);
126
127        if ((key = oath_key_from_file(keyfile)) == NULL)
128                return (RET_ERROR);
129        printf("%s\n", oath_key_to_uri(key));
130        oath_key_free(key);
131        return (RET_SUCCESS);
132}
133
134/*
135 * Check whether a given response is correct for the given keyfile.
136 */
137static int
138oathkey_verify(int argc, char *argv[])
139{
140        struct oath_key *key;
141        unsigned long response;
142        char *end;
143        int match;
144
145        if (argc < 1)
146                return (RET_USAGE);
147        if ((key = oath_key_from_file(keyfile)) == NULL)
148                return (RET_ERROR);
149        response = strtoul(*argv, &end, 10);
150        if (end == *argv || *end != '\0')
151                response = ULONG_MAX; /* never valid */
152        if (key->mode == om_totp)
153                match = oath_totp_match(key, response, 3 /* XXX window */);
154        else if (key->mode == om_hotp)
155                match = oath_hotp_match(key, response, 17 /* XXX window */);
156        else
157                match = -1;
158        /* oath_*_match() return -1 on error, 0 on failure, 1 on success */
159        if (match < 0) {
160                warnx("OATH error");
161                match = 0;
162        }
163        if (verbose)
164                warnx("response: %lu %s", response,
165                    match ? "matched" : "did not match");
166        if (match) {
167                /* XXX write key back! */
168        }
169        oath_key_free(key);
170        return (match ? RET_SUCCESS : RET_FAILURE);
171}
172
173/*
174 * Print usage string and exit.
175 */
176static void
177usage(void)
178{
179        fprintf(stderr,
180            "usage: oathkey [-hv] [-u user] [-k keyfile] <command>\n"
181            "\n"
182            "Commands:\n"
183            "    genkey      Generate a new key\n"
184            "    setkey      Generate a new key\n"
185            "    uri         Print the key in otpauth URI form\n"
186            "    verify <response>\n"
187            "                Verify a response\n");
188        exit(1);
189}
190
191int
192main(int argc, char *argv[])
193{
194        struct passwd *pw;
195        int opt, ret;
196        char *cmd;
197
198        /*
199         * Parse command-line options
200         */
201        while ((opt = getopt(argc, argv, "hk:u:v")) != -1)
202                switch (opt) {
203                case 'k':
204                        keyfile = optarg;
205                        break;
206                case 'u':
207                        user = optarg;
208                        break;
209                case 'v':
210                        ++verbose;
211                        break;
212                case 'h':
213                default:
214                        usage();
215                }
216
217        argc -= optind;
218        argv += optind;
219
220        if (argc-- < 1)
221                usage();
222        cmd = *argv++;
223
224        /*
225         * Check whether we are (really!) root.
226         */
227        if (getuid() == 0)
228                isroot = 1;
229
230        /*
231         * If a user was specified on the command line, check whether it
232         * matches our real UID.
233         */
234        if (user != NULL) {
235                if ((pw = getpwnam(user)) == NULL)
236                        errx(1, "no such user");
237                if (getuid() == pw->pw_uid)
238                        issameuser = 1;
239        }
240
241        /*
242         * If no user was specified on the command line, look up the user
243         * that corresponds to our real UID.
244         */
245        if (user == NULL) {
246                if ((pw = getpwuid(getuid())) == NULL)
247                        errx(1, "who are you?");
248                if (asprintf(&user, "%s", pw->pw_name) < 0)
249                        err(1, "asprintf()");
250        }
251
252        /*
253         * If no keyfile was specified on the command line, derive it from
254         * the user name.
255         */
256        if (keyfile == NULL)
257                /* XXX replace with a function that searches multiple locations? */
258                if (asprintf(&keyfile, "/var/oath/%s.otpauth", user) < 0)
259                        err(1, "asprintf()");
260
261        /*
262         * Execute the requested command
263         */
264        if (strcmp(cmd, "help") == 0)
265                ret = RET_USAGE;
266        else if (strcmp(cmd, "genkey") == 0)
267                ret = oathkey_genkey(argc, argv);
268        else if (strcmp(cmd, "setkey") == 0)
269                ret = oathkey_setkey(argc, argv);
270        else if (strcmp(cmd, "uri") == 0)
271                ret = oathkey_uri(argc, argv);
272        else if (strcmp(cmd, "verify") == 0)
273                ret = oathkey_verify(argc, argv);
274        else
275                ret = RET_USAGE;
276
277        /*
278         * Check result and act accordingly
279         */
280        switch (ret) {
281        case RET_UNAUTH:
282                errno = EPERM;
283                err(1, "%s", cmd);
284                break;
285        case RET_USAGE:
286                usage();
287                break;
288        case RET_SUCCESS:
289                exit(0);
290                break;
291        case RET_FAILURE:
292                exit(1);
293                break;
294        case RET_ERROR:
295                exit(2);
296                break;
297        default:
298                exit(3);
299                break;
300        }
301        /* not reached */
302        exit(255);
303}
Note: See TracBrowser for help on using the repository browser.