1 /*
2 * auth-otp: HOTP/TOTP tool for ProFTPD mod_auth_otp module
3 * Copyright 2016 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, The ProFTPD Project and other respective copyright
20 * holders give permission to link this program with OpenSSL, and distribute
21 * the resulting executable, without including the source code for OpenSSL in
22 * the source distribution.
23 */
24
25 #include "config.h"
26
27 #include <ctype.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34
35 #ifdef HAVE_GETOPT_H
36 # include <getopt.h>
37 #else
38 # include "../lib/getopt.h"
39 #endif /* !HAVE_GETOPT_H */
40
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44
45 #ifdef HAVE_NETINET_IN_H
46 # include <netinet/in.h>
47 #endif
48
49 #include "mod_auth_otp.h"
50 #include "pool.h"
51 #include "base32.h"
52 #include "crypto.h"
53 #include "otp.h"
54
55 static int quiet = FALSE, verbose = FALSE;
56
57 /* Necessary stubs. */
58
59 int auth_otp_logfd = -1;
60 pool *auth_otp_pool = NULL;
61
pr_alarms_block(void)62 void pr_alarms_block(void) {
63 }
64
pr_alarms_unblock(void)65 void pr_alarms_unblock(void) {
66 }
67
pr_log_pri(int prio,const char * fmt,...)68 void pr_log_pri(int prio, const char *fmt, ...) {
69 if (verbose) {
70 va_list msg;
71
72 fprintf(stderr, "PRI%d: ", prio);
73
74 va_start(msg, fmt);
75 vfprintf(stderr, fmt, msg);
76 va_end(msg);
77
78 fprintf(stderr, "\n");
79 }
80 }
81
pr_log_writefile(int fd,const char * prefix,const char * fmt,...)82 int pr_log_writefile(int fd, const char *prefix, const char *fmt, ...) {
83 if (verbose) {
84 va_list msg;
85
86 fprintf(stderr, "%s: ", prefix);
87
88 va_start(msg, fmt);
89 vfprintf(stderr, fmt, msg);
90 va_end(msg);
91
92 fprintf(stderr, "\n");
93 }
94
95 return 0;
96 }
97
pr_memscrub(void * ptr,size_t ptrlen)98 void pr_memscrub(void *ptr, size_t ptrlen) {
99 if (ptr == NULL ||
100 ptrlen == 0) {
101 return;
102 }
103
104 OPENSSL_cleanse(ptr, ptrlen);
105 }
106
pr_module_get(const char * name)107 module *pr_module_get(const char *name) {
108 errno = ENOENT;
109 return NULL;
110 }
111
pr_signals_handle(void)112 void pr_signals_handle(void) {
113 }
114
pr_snprintf(char * buf,size_t bufsz,const char * fmt,...)115 int pr_snprintf(char *buf, size_t bufsz, const char *fmt, ...) {
116 va_list msg;
117 int res;
118
119 va_start(msg, fmt);
120 res = pr_vsnprintf(buf, bufsz, fmt, msg);
121 va_end(msg);
122
123 return res;
124 }
125
pr_trace_msg(const char * name,int level,const char * fmt,...)126 int pr_trace_msg(const char *name, int level, const char *fmt, ...) {
127 if (verbose) {
128 va_list msg;
129
130 fprintf(stderr, "<%s:%d>: ", name, level);
131
132 va_start(msg, fmt);
133 vfprintf(stderr, fmt, msg);
134 va_end(msg);
135
136 fprintf(stderr, "\n");
137 }
138
139 return 0;
140 }
141
pr_vsnprintf(char * buf,size_t bufsz,const char * fmt,va_list msg)142 int pr_vsnprintf(char *buf, size_t bufsz, const char *fmt, va_list msg) {
143 return vsnprintf(buf, bufsz, fmt, msg);
144 }
145
146 static struct option_help {
147 const char *long_opt, *short_opt, *desc;
148 } opts_help[] = {
149 { "--help", "-h", NULL },
150 { "--quiet", "-q", NULL },
151 { "--verbose","-v", NULL },
152 { NULL }
153 };
154
155 #ifdef HAVE_GETOPT_LONG
156 static struct option opts[] = {
157 { "help", 0, NULL, 'h' },
158 { "quiet", 0, NULL, 'q' },
159 { "verbose", 0, NULL, 'v' },
160 { NULL, 0, NULL, 0 }
161 };
162 #endif /* HAVE_GETOPT_LONG */
163
show_usage(const char * progname,int exit_code)164 static void show_usage(const char *progname, int exit_code) {
165 struct option_help *h = NULL;
166
167 printf("usage: %s [options]\n", progname);
168 for (h = opts_help; h->long_opt; h++) {
169 #ifdef HAVE_GETOPT_LONG
170 printf(" %s, %s\n", h->short_opt, h->long_opt);
171 #else /* HAVE_GETOPT_LONG */
172 printf(" %s\n", h->short_opt);
173 #endif
174 if (h->desc == NULL) {
175 printf(" display %s usage\n", progname);
176
177 } else {
178 printf(" %s\n", h->desc);
179 }
180 }
181
182 exit(exit_code);
183 }
184
generate_code(pool * p,const char * key,size_t key_len)185 static int generate_code(pool *p, const char *key, size_t key_len) {
186 int res;
187 unsigned int code;
188
189 res = auth_otp_hotp(p, (const unsigned char *) key, key_len, 0, &code);
190 if (res < 0) {
191 return -1;
192 }
193
194 return (int) code;
195 }
196
generate_secret(pool * p)197 static const char *generate_secret(pool *p) {
198 int res;
199 unsigned char encoded[18], rnd[32];
200 size_t encoded_len;
201 const char *secret;
202 const unsigned char *ptr;
203
204 if (RAND_bytes(rnd, sizeof(rnd)) != 1) {
205 fprintf(stderr, "Error obtaining %lu bytes of random data:\n",
206 (unsigned long) sizeof(rnd));
207 ERR_print_errors_fp(stderr);
208 errno = EPERM;
209 return NULL;
210 }
211
212 encoded_len = sizeof(encoded);
213 ptr = encoded;
214 res = auth_otp_base32_encode(p, rnd, sizeof(rnd), &ptr, &encoded_len);
215 if (res < 0) {
216 return NULL;
217 }
218
219 secret = pstrndup(p, (const char *) ptr, encoded_len);
220 return secret;
221 }
222
main(int argc,char ** argv)223 int main(int argc, char **argv) {
224 int c = 0;
225 char *ptr, *progname = *argv;
226 const char *cmdopts = "hqv", *secret = NULL;
227
228 ptr = strrchr(progname, '/');
229 if (ptr != NULL) {
230 progname = ptr+1;
231 }
232
233 opterr = 0;
234 while ((c =
235 #ifdef HAVE_GETOPT_LONG
236 getopt_long(argc, argv, cmdopts, opts, NULL)
237 #else /* HAVE_GETOPT_LONG */
238 getopt(argc, argv, cmdopts)
239 #endif /* HAVE_GETOPT_LONG */
240 ) != -1) {
241 switch (c) {
242 case 'h':
243 show_usage(progname, 0);
244 break;
245
246 case 'q':
247 verbose = FALSE;
248 quiet = TRUE;
249 break;
250
251 case 'v':
252 quiet = FALSE;
253 verbose = TRUE;
254 break;
255
256 case '?':
257 fprintf(stderr, "unknown option: %c\n", (char) optopt);
258 show_usage(progname, 1);
259 break;
260 }
261 }
262
263 auth_otp_pool = make_sub_pool(NULL);
264
265 #if OPENSSL_VERSION_NUMBER < 0x10100000L
266 OPENSSL_config(NULL);
267 #endif /* prior to OpenSSL-1.1.x */
268 ERR_load_crypto_strings();
269 OpenSSL_add_all_algorithms();
270
271 secret = generate_secret(auth_otp_pool);
272 if (secret == NULL) {
273 return 1;
274 }
275
276 if (quiet) {
277 fprintf(stdout, "%s\n", secret);
278
279 } else {
280 int code;
281
282 code = generate_code(auth_otp_pool, secret, strlen(secret));
283 if (code < 0) {
284 fprintf(stderr, "%s: error generating verification code: %s\n", progname,
285 strerror(errno));
286 destroy_pool(auth_otp_pool);
287 return 1;
288 }
289
290 fprintf(stdout, "-------------------------------------------------\n");
291 fprintf(stdout, "Your new secret key is: %s\n\n", secret);
292 fprintf(stdout, "To add this key to your SQL table, you might use:\n\n");
293 fprintf(stdout, " INSERT INTO auth_otp (secret, counter) VALUES ('%s', 0);\n\n",
294 secret);
295 fprintf(stdout, "Your verification code is: %06d\n", code);
296 fprintf(stdout, "-------------------------------------------------\n");
297 }
298
299 ERR_free_strings();
300 EVP_cleanup();
301 RAND_cleanup();
302
303 destroy_pool(auth_otp_pool);
304 return 0;
305 }
306