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