1 /*
2  * ProFTPD: mod_auth_otp
3  * Copyright (c) 2015-2017 TJ Saunders
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, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  *
24  * -----DO NOT EDIT BELOW THIS LINE-----
25  * $Archive: mod_auth_otp.a $
26  * $Libraries: -lcrypto$
27  */
28 
29 #include "mod_auth_otp.h"
30 #if defined(HAVE_SFTP)
31 # include "mod_sftp.h"
32 #endif /* HAVE_SFTP */
33 #include "db.h"
34 #include "otp.h"
35 
36 /* mod_auth_otp option flags */
37 #define AUTH_OTP_OPT_STANDARD_RESPONSE		0x001
38 #define AUTH_OTP_OPT_REQUIRE_TABLE_ENTRY	0x002
39 #define AUTH_OTP_OPT_DISPLAY_VERIFICATION_CODE	0x004
40 
41 #define AUTH_OTP_VERIFICATION_CODE_PROMPT	"Verification code: "
42 
43 /* From src/response.c */
44 extern pr_response_t *resp_list;
45 
46 pool *auth_otp_pool = NULL;
47 int auth_otp_logfd = -1;
48 unsigned long auth_otp_opts = 0UL;
49 module auth_otp_module;
50 
51 static authtable auth_otp_authtab[3];
52 static int auth_otp_engine = FALSE;
53 static unsigned int auth_otp_algo = AUTH_OTP_ALGO_TOTP_SHA1;
54 static struct auth_otp_db *dbh = NULL;
55 static config_rec *auth_otp_db_config = NULL;
56 static int auth_otp_auth_code = PR_AUTH_BADPWD;
57 
58 /* Necessary prototypes */
59 static int auth_otp_sess_init(void);
60 static int handle_user_otp(pool *p, const char *user, const char *user_otp,
61   int authoritative);
62 
63 #if defined(HAVE_SFTP)
64 /* mod_sftp support */
65 static int auth_otp_using_sftp = FALSE;
66 static sftp_kbdint_driver_t auth_otp_kbdint_driver;
67 #endif /* HAVE_SFTP */
68 
69 static const char *trace_channel = "auth_otp";
70 
71 #if defined(HAVE_SFTP)
auth_otp_kbdint_open(sftp_kbdint_driver_t * driver,const char * user)72 static int auth_otp_kbdint_open(sftp_kbdint_driver_t *driver,
73     const char *user) {
74   const char *tabinfo;
75   int xerrno;
76 
77   tabinfo = auth_otp_db_config->argv[0];
78 
79   PRIVS_ROOT
80   dbh = auth_otp_db_open(driver->driver_pool, tabinfo);
81   xerrno = errno;
82   PRIVS_RELINQUISH
83 
84   if (dbh == NULL) {
85     pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
86       "unable to open AuthOTPTable: %s", strerror(xerrno));
87     errno = xerrno;
88     return -1;
89   }
90 
91   driver->driver_pool = make_sub_pool(auth_otp_pool);
92   pr_pool_tag(driver->driver_pool, "AuthOTP keyboard-interactive driver pool");
93 
94   return 0;
95 }
96 
auth_otp_kbdint_authenticate(sftp_kbdint_driver_t * driver,const char * user)97 static int auth_otp_kbdint_authenticate(sftp_kbdint_driver_t *driver,
98     const char *user) {
99   int authoritative = FALSE, res, xerrno;
100   sftp_kbdint_challenge_t *challenge;
101   unsigned int recvd_count = 0;
102   const char **recvd_responses = NULL, *user_otp = NULL;
103 
104   if (auth_otp_authtab[0].auth_flags & PR_AUTH_FL_REQUIRED) {
105     authoritative = TRUE;
106   }
107 
108   /* Check first to see if we even have information for this user, for to
109    * use when verifying them.  If not, then don't prompt the user for info
110    * that we know, a priori, we cannot verify.
111    */
112   res = auth_otp_db_rlock(dbh);
113   if (res < 0) {
114     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
115       "failed to read-lock AuthOTPTable: %s", strerror(errno));
116   }
117 
118   res = auth_otp_db_have_user_info(driver->driver_pool, dbh, user);
119   xerrno = errno;
120 
121   if (auth_otp_db_unlock(dbh) < 0) {
122     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
123       "failed to unlock AuthOTPTable: %s", strerror(errno));
124   }
125 
126   if (res < 0) {
127     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
128       "no info for user '%s' found in AuthOTPTable, skipping "
129       "SSH2 keyboard-interactive challenge", user);
130     errno = xerrno;
131     return -1;
132   }
133 
134   challenge = pcalloc(driver->driver_pool, sizeof(sftp_kbdint_challenge_t));
135   challenge->challenge = pstrdup(driver->driver_pool,
136     AUTH_OTP_VERIFICATION_CODE_PROMPT);
137   challenge->display_response = FALSE;
138 
139   if (auth_otp_opts & AUTH_OTP_OPT_DISPLAY_VERIFICATION_CODE) {
140     challenge->display_response = TRUE;
141   }
142 
143   if (sftp_kbdint_send_challenge(NULL, NULL, 1, challenge) < 0) {
144     xerrno = errno;
145 
146     pr_trace_msg(trace_channel, 3,
147       "error sending keyboard-interactive challenges: %s", strerror(xerrno));
148 
149     errno = xerrno;
150     return -1;
151   }
152 
153   if (sftp_kbdint_recv_response(driver->driver_pool, 1,
154       &recvd_count, &recvd_responses) < 0) {
155     xerrno = errno;
156 
157     pr_trace_msg(trace_channel, 3,
158       "error receiving keyboard-interactive responses: %s", strerror(xerrno));
159 
160     errno = xerrno;
161     return -1;
162   }
163 
164   user_otp = recvd_responses[0];
165   res = handle_user_otp(driver->driver_pool, user, user_otp, authoritative);
166   if (res == 1) {
167     return 0;
168   }
169 
170   errno = EPERM;
171   return -1;
172 }
173 
auth_otp_kbdint_close(sftp_kbdint_driver_t * driver)174 static int auth_otp_kbdint_close(sftp_kbdint_driver_t *driver) {
175   if (dbh != NULL) {
176     if (auth_otp_db_close(dbh) < 0) {
177       (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
178         "error closing AuthOTPTable: %s", strerror(errno));
179     }
180 
181     dbh = NULL;
182   }
183 
184   if (driver->driver_pool) {
185     destroy_pool(driver->driver_pool);
186     driver->driver_pool = NULL;
187   }
188 
189   return 0;
190 }
191 #endif /* HAVE_SFTP */
192 
check_otp_code(pool * p,const char * user,const char * user_otp,const unsigned char * secret,size_t secret_len,unsigned long counter)193 static int check_otp_code(pool *p, const char *user, const char *user_otp,
194     const unsigned char *secret, size_t secret_len, unsigned long counter) {
195   int res;
196   char code_str[9];
197   unsigned int code;
198 
199   switch (auth_otp_algo) {
200     case AUTH_OTP_ALGO_TOTP_SHA1:
201     case AUTH_OTP_ALGO_TOTP_SHA256:
202     case AUTH_OTP_ALGO_TOTP_SHA512:
203       res = auth_otp_totp(p, secret, secret_len, counter, auth_otp_algo, &code);
204       if (res < 0) {
205         pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
206           "error generating TOTP code for user '%s': %s", user,
207           strerror(errno));
208       }
209       break;
210 
211     case AUTH_OTP_ALGO_HOTP:
212       res = auth_otp_hotp(p, secret, secret_len, counter, &code);
213       if (res < 0) {
214         pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
215           "error generating HOTP code for user '%s': %s", user,
216           strerror(errno));
217       }
218       break;
219 
220     default:
221       errno = EINVAL;
222       res = -1;
223       break;
224   }
225 
226   if (res < 0) {
227     return -1;
228   }
229 
230   memset(code_str, '\0', sizeof(code_str));
231 
232   /* Note: If/when more than 6 digits are needed, the following format string
233    * would need to change to match.
234    */
235   pr_snprintf(code_str, sizeof(code_str)-1, "%06u", code);
236 
237   pr_trace_msg(trace_channel, 13,
238     "computed code '%s', client sent code '%s'", code_str, user_otp);
239 
240   res = pr_auth_check(p, code_str, user, user_otp);
241   if (res == PR_AUTH_OK ||
242       res == PR_AUTH_RFC2228_OK) {
243     return 0;
244   }
245 
246   return -1;
247 }
248 
update_otp_counter(pool * p,const char * user,unsigned long next_counter)249 static int update_otp_counter(pool *p, const char *user,
250     unsigned long next_counter) {
251   int res = 0;
252 
253   if (auth_otp_algo == AUTH_OTP_ALGO_HOTP) {
254     int lock;
255 
256     lock = auth_otp_db_wlock(dbh);
257     if (lock < 0) {
258       (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
259         "failed to write-lock AuthOTPTable: %s", strerror(errno));
260     }
261 
262     res = auth_otp_db_update_counter(dbh, user, next_counter);
263     if (res < 0) {
264       (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
265         "error updating AuthOTPTable for HOTP counter for user '%s': %s",
266         user, strerror(errno));
267     }
268 
269     lock = auth_otp_db_unlock(dbh);
270     if (lock < 0) {
271       (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
272         "failed to unlock AuthOTPTable: %s", strerror(errno));
273     }
274   }
275 
276   return res;
277 }
278 
279 /* Sets the auth_otp_auth_code variable upon failure. */
handle_user_otp(pool * p,const char * user,const char * user_otp,int authoritative)280 static int handle_user_otp(pool *p, const char *user, const char *user_otp,
281     int authoritative) {
282   int res = 0, xerrno = 0;
283   const unsigned char *secret = NULL;
284   size_t secret_len = 0;
285   unsigned long counter = 0, *counter_ptr = NULL, next_counter = 0;
286 
287   if (user_otp == NULL ||
288       (strlen(user_otp) == 0)) {
289     pr_trace_msg(trace_channel, 1,
290       "no OTP code provided by user, rejecting");
291     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
292       "FAILED: user '%s' provided invalid OTP code", user);
293     auth_otp_auth_code = PR_AUTH_BADPWD;
294     return -1;
295   }
296 
297   switch (auth_otp_algo) {
298     case AUTH_OTP_ALGO_TOTP_SHA1:
299     case AUTH_OTP_ALGO_TOTP_SHA256:
300     case AUTH_OTP_ALGO_TOTP_SHA512: {
301       counter = (unsigned long) time(NULL);
302       break;
303     }
304 
305     case AUTH_OTP_ALGO_HOTP:
306       counter_ptr = &counter;
307       break;
308 
309     default:
310       pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
311         "unsupported AuthOTPAlgorithm configured");
312       return 0;
313   }
314 
315   res = auth_otp_db_rlock(dbh);
316   if (res < 0) {
317     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
318       "failed to read-lock AuthOTPTable: %s", strerror(errno));
319   }
320 
321   res = auth_otp_db_get_user_info(p, dbh, user, &secret, &secret_len,
322     counter_ptr);
323   xerrno = errno;
324 
325   if (auth_otp_db_unlock(dbh) < 0) {
326     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
327       "failed to unlock AuthOTPTable: %s", strerror(errno));
328   }
329 
330   if (res < 0) {
331     if (xerrno == ENOENT) {
332       pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
333         "user '%s' has no OTP info in AuthOTPTable", user);
334 
335     } else {
336       pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
337         "unable to retrieve OTP info for user '%s': %s", user,
338         strerror(xerrno));
339     }
340 
341     /* If there was no entry found in the table (errno = ENOENT), should we
342      * returned ERROR or DECLINED?
343      *
344      * If we are not authoritative, then we returned DECLINED, regardless of
345      * the errno value, in order to allow other modules a chance at handling
346      * the authentication.  This module can only be "authoritative" about
347      * OTP codes it can generate, and if there is no entry in the table,
348      * REQUIRE_TABLE_ENTRY option or not, we cannot generate a code for
349      * comparisons.
350      *
351      * If we ARE authoritative, and the REQUIRE_TABLE_ENTRY option is in
352      * effect, then we return ERROR -- this is how we require OTP codes for
353      * ALL users.  Otherwise we return DECLINED, despite being authoritative,
354      * because again, we don't have the necessary data for computing the code.
355      */
356 
357     if (authoritative) {
358       if (auth_otp_opts & AUTH_OTP_OPT_REQUIRE_TABLE_ENTRY) {
359         (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
360           "FAILED: user '%s' does not have entry in OTP tables", user);
361         auth_otp_auth_code = PR_AUTH_BADPWD;
362         return -1;
363       }
364     }
365 
366     return 0;
367   }
368 
369   res = check_otp_code(p, user, user_otp, secret, secret_len, counter);
370   if (res == 0) {
371     pr_memscrub((char *) secret, secret_len);
372 
373     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
374       "SUCCESS: user '%s' provided valid OTP code", user);
375 
376     /* XXX Update state with details about the expected OTP found,
377      * e.g. for clock drift.
378      */
379     update_otp_counter(p, user, counter + 1);
380     return 1;
381   }
382 
383   /* We SHOULD be allowing for clock skew/counter drift here.  We
384    * currently check one window ahead AND behind; is this policy too lenient?
385    * RFC 6238, Section 5.2 recommends one window for network transmission
386    * delay (which assumes that the client's OTP is always "behind" the
387    * server's OTP).
388    *
389    * By checking one window ahead/behind, we allow for clock skew in either
390    * direction: server ahead of client (most likely), client ahead of server.
391    */
392   pr_trace_msg(trace_channel, 3,
393     "current counter check failed, checking one window behind");
394 
395   switch (auth_otp_algo) {
396     case AUTH_OTP_ALGO_TOTP_SHA1:
397     case AUTH_OTP_ALGO_TOTP_SHA256:
398     case AUTH_OTP_ALGO_TOTP_SHA512:
399       next_counter = counter - AUTH_OTP_TOTP_TIMESTEP_SECS;
400       break;
401 
402     case AUTH_OTP_ALGO_HOTP:
403       next_counter = counter - 1;
404       break;
405   }
406 
407   res = check_otp_code(p, user, user_otp, secret, secret_len, next_counter);
408   if (res == 0) {
409     pr_memscrub((char *) secret, secret_len);
410 
411     pr_trace_msg(trace_channel, 3,
412       "counter check SUCCEEDED for one counter window behind; client is "
413       "out-of-sync");
414 
415     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
416       "SUCCESS: user '%s' provided valid OTP code", user);
417 
418     /* XXX Update state with details about the expected OTP found,
419      * e.g. for clock drift, event counter increment, etc.
420      *
421      * Note that here, the client is "behind".  Expected for TOTP, but not
422      * for HOTP.  Hmm.
423      */
424     update_otp_counter(p, user, counter + 1);
425     return 1;
426   }
427 
428   pr_trace_msg(trace_channel, 3,
429     "counter one window ahead check failed, checking one window ahead");
430 
431   switch (auth_otp_algo) {
432     case AUTH_OTP_ALGO_TOTP_SHA1:
433     case AUTH_OTP_ALGO_TOTP_SHA256:
434     case AUTH_OTP_ALGO_TOTP_SHA512:
435       next_counter = counter + AUTH_OTP_TOTP_TIMESTEP_SECS;
436       break;
437 
438     case AUTH_OTP_ALGO_HOTP:
439       next_counter = counter + 1;
440       break;
441   }
442 
443   res = check_otp_code(p, user, user_otp, secret, secret_len, next_counter);
444   if (res == 0) {
445     pr_memscrub((char *) secret, secret_len);
446 
447     pr_trace_msg(trace_channel, 3,
448       "counter check SUCCEEDED for one counter window ahead; client is "
449       "out-of-sync");
450 
451     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
452       "SUCCESS: user '%s' provided valid OTP code", user);
453 
454     /* XXX Update state with details about the expected OTP found,
455      * e.g. for clock drift, event counter increment, etc.
456      *
457      * Note that here, the client is "ahead".  NOT expected for TOTP, but is
458      * for HOTP.  Hmm.
459      */
460     update_otp_counter(p, user, counter + 1);
461     return 1;
462   }
463 
464   pr_memscrub((char *) secret, secret_len);
465 
466   if (authoritative) {
467     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
468       "FAILED: user '%s' provided invalid OTP code", user);
469     auth_otp_auth_code = PR_AUTH_BADPWD;
470     return -1;
471   }
472 
473   return 0;
474 }
475 
476 /* Authentication handlers
477  */
478 
auth_otp_auth(cmd_rec * cmd)479 MODRET auth_otp_auth(cmd_rec *cmd) {
480   int authoritative = FALSE, res = 0;
481   char *user = NULL, *user_otp = NULL;
482 
483   if (auth_otp_engine == FALSE ||
484       dbh == NULL) {
485     return PR_DECLINED(cmd);
486   }
487 
488   user = cmd->argv[0];
489   user_otp = cmd->argv[1];
490 
491   /* Figure out our default return style: whether or not we should allow
492    * other auth modules a shot at this user or not is controlled by adding
493    * '*' to a module name in the AuthOrder directive.  By default, auth
494    * modules are not authoritative, and allow other auth modules a chance at
495    * authenticating the user.  This is not the most secure configuration, but
496    * it allows things like AuthUserFile to work "out of the box".
497    */
498   if (auth_otp_authtab[0].auth_flags & PR_AUTH_FL_REQUIRED) {
499     authoritative = TRUE;
500   }
501 
502 #if defined(HAVE_SFTP)
503   if (auth_otp_using_sftp) {
504     const char *proto = NULL;
505 
506     proto = pr_session_get_protocol(0);
507     if (strcmp(proto, "ssh2") == 0) {
508       /* We should already have done the keyboard-interactive challenge by
509        * this point in the session.
510        */
511 
512       if (auth_otp_auth_code != PR_AUTH_OK &&
513           auth_otp_auth_code != PR_AUTH_RFC2228_OK) {
514         if (authoritative) {
515           /* Indicate ERROR. */
516           res = -1;
517 
518         } else {
519           /* Indicate DECLINED. */
520           res = 0;
521         }
522 
523       } else {
524         /* Indicate HANDLED. */
525         res = 1;
526       }
527 
528     } else {
529       res = handle_user_otp(cmd->tmp_pool, user, user_otp, authoritative);
530     }
531 
532   } else {
533     res = handle_user_otp(cmd->tmp_pool, user, user_otp, authoritative);
534   }
535 #else
536   res = handle_user_otp(cmd->tmp_pool, user, user_otp, authoritative);
537 #endif /* HAVE_SFTP */
538 
539   if (res == 1) {
540     session.auth_mech = "mod_auth_otp.c";
541     return PR_HANDLED(cmd);
542 
543   } else if (res < 0) {
544     return PR_ERROR_INT(cmd, auth_otp_auth_code);
545   }
546 
547   return PR_DECLINED(cmd);
548 }
549 
auth_otp_chkpass(cmd_rec * cmd)550 MODRET auth_otp_chkpass(cmd_rec *cmd) {
551   const char *real_otp, *user, *user_otp;
552 
553   if (auth_otp_engine == FALSE) {
554     return PR_DECLINED(cmd);
555   }
556 
557   real_otp = cmd->argv[0];
558   user = cmd->argv[1];
559   user_otp = cmd->argv[2];
560 
561   if (strcmp(real_otp, user_otp) == 0) {
562     return PR_HANDLED(cmd);
563   }
564 
565   switch (auth_otp_algo) {
566     case AUTH_OTP_ALGO_TOTP_SHA1:
567       pr_trace_msg(trace_channel, 9,
568         "expected TOTP-SHA1 '%s', got '%s' for user '%s'", real_otp, user_otp,
569         user);
570       break;
571 
572     case AUTH_OTP_ALGO_TOTP_SHA256:
573       pr_trace_msg(trace_channel, 9,
574         "expected TOTP-SHA256 '%s', got '%s' for user '%s'", real_otp, user_otp,
575         user);
576       break;
577 
578     case AUTH_OTP_ALGO_TOTP_SHA512:
579       pr_trace_msg(trace_channel, 9,
580         "expected TOTP-SHA512 '%s', got '%s' for user '%s'", real_otp, user_otp,
581         user);
582       break;
583 
584     case AUTH_OTP_ALGO_HOTP:
585       pr_trace_msg(trace_channel, 9,
586         "expected HOTP '%s', got '%s' for user '%s'", real_otp, user_otp, user);
587       break;
588   }
589 
590   return PR_DECLINED(cmd);
591 }
592 
593 /* Configuration handlers
594  */
595 
596 /* usage: AuthOTPAlgorithm algo */
set_authotpalgo(cmd_rec * cmd)597 MODRET set_authotpalgo(cmd_rec *cmd) {
598   int algo = -1;
599   config_rec *c = NULL;
600 
601   CHECK_ARGS(cmd, 1);
602   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
603 
604   if (strcasecmp(cmd->argv[1], "hotp") == 0) {
605     algo = AUTH_OTP_ALGO_HOTP;
606 
607   } else if (strcasecmp(cmd->argv[1], "totp") == 0 ||
608              strcasecmp(cmd->argv[1], "totp-sha1") == 0) {
609     algo = AUTH_OTP_ALGO_TOTP_SHA1;
610 
611 #ifdef HAVE_SHA256_OPENSSL
612   } else if (strcasecmp(cmd->argv[1], "totp-sha256") == 0) {
613     algo = AUTH_OTP_ALGO_TOTP_SHA256;
614 #endif /* SHA256 OpenSSL support */
615 
616 #ifdef HAVE_SHA512_OPENSSL
617   } else if (strcasecmp(cmd->argv[1], "totp-sha512") == 0) {
618     algo = AUTH_OTP_ALGO_TOTP_SHA512;
619 #endif /* SHA512 OpenSSL support */
620 
621   } else {
622     CONF_ERROR(cmd, "expected supported OTP algorithm");
623   }
624 
625   c = add_config_param(cmd->argv[0], 1, NULL);
626   c->argv[0] = pcalloc(c->pool, sizeof(int));
627   *((int *) c->argv[0]) = algo;
628 
629   return PR_HANDLED(cmd);
630 }
631 
632 /* usage: AuthOTPEngine on|off */
set_authotpengine(cmd_rec * cmd)633 MODRET set_authotpengine(cmd_rec *cmd) {
634   int engine = -1;
635   config_rec *c = NULL;
636 
637   CHECK_ARGS(cmd, 1);
638   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
639 
640   engine = get_boolean(cmd, 1);
641   if (engine == -1) {
642     CONF_ERROR(cmd, "expected Boolean parameter");
643   }
644 
645   c = add_config_param(cmd->argv[0], 1, NULL);
646   c->argv[0] = pcalloc(c->pool, sizeof(int));
647   *((int *) c->argv[0]) = engine;
648 
649   return PR_HANDLED(cmd);
650 }
651 
652 /* usage: AuthOTPLog path|"none" */
set_authotplog(cmd_rec * cmd)653 MODRET set_authotplog(cmd_rec *cmd) {
654   CHECK_ARGS(cmd, 1);
655   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
656 
657   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
658   return PR_HANDLED(cmd);
659 }
660 
661 /* usage: AuthOTPOptions opt1 opt2 ... */
set_authotpoptions(cmd_rec * cmd)662 MODRET set_authotpoptions(cmd_rec *cmd) {
663   config_rec *c = NULL;
664   register unsigned int i = 0;
665   unsigned long opts = 0UL;
666 
667   if (cmd->argc-1 == 0) {
668     CONF_ERROR(cmd, "wrong number of parameters");
669   }
670 
671   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
672 
673   c = add_config_param(cmd->argv[0], 1, NULL);
674 
675   for (i = 1; i < cmd->argc; i++) {
676     if (strcmp(cmd->argv[i], "StandardResponse") == 0) {
677       opts |= AUTH_OTP_OPT_STANDARD_RESPONSE;
678 
679     } else if (strcmp(cmd->argv[i], "RequireTableEntry") == 0) {
680       opts |= AUTH_OTP_OPT_REQUIRE_TABLE_ENTRY;
681 
682     } else if (strcmp(cmd->argv[i], "DisplayVerificationCode") == 0) {
683       opts |= AUTH_OTP_OPT_DISPLAY_VERIFICATION_CODE;
684 
685     } else {
686       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown AuthOTPOption: '",
687         cmd->argv[i], "'", NULL));
688     }
689   }
690 
691   c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
692   *((unsigned long *) c->argv[0]) = opts;
693 
694   return PR_HANDLED(cmd);
695 }
696 
697 /* usage: AuthOTPTable sql:/... */
set_authotptable(cmd_rec * cmd)698 MODRET set_authotptable(cmd_rec *cmd) {
699   char *ptr = NULL;
700 
701   CHECK_ARGS(cmd, 1);
702   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
703 
704   /* Separate the parameter into the separate pieces.  The parameter is
705    * given as one string to enhance its similarity to URL syntax.
706    */
707   ptr = strchr(cmd->argv[1], ':');
708   if (ptr == NULL) {
709     CONF_ERROR(cmd, "badly formatted parameter");
710   }
711 
712   if (strncasecmp(cmd->argv[1], "sql:/", 5) != 0) {
713     CONF_ERROR(cmd, "badly formatted parameter");
714   }
715 
716   *ptr++ = '\0';
717 
718   add_config_param_str(cmd->argv[0], 1, ptr);
719   return PR_HANDLED(cmd);
720 }
721 
722 /* usage: AuthOTPTableLock path */
set_authotptablelock(cmd_rec * cmd)723 MODRET set_authotptablelock(cmd_rec *cmd) {
724   CHECK_ARGS(cmd, 1);
725   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
726 
727   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
728   return PR_HANDLED(cmd);
729 }
730 
731 /* Command handlers
732  */
733 
auth_otp_post_pass(cmd_rec * cmd)734 MODRET auth_otp_post_pass(cmd_rec *cmd) {
735   if (auth_otp_engine == FALSE) {
736     return PR_DECLINED(cmd);
737   }
738 
739   if (dbh != NULL) {
740     if (auth_otp_db_close(dbh) < 0) {
741       (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
742         "error closing AuthOTPTable: %s", strerror(errno));
743     }
744 
745     dbh = NULL;
746   }
747 
748   return PR_DECLINED(cmd);
749 }
750 
auth_otp_pre_user(cmd_rec * cmd)751 MODRET auth_otp_pre_user(cmd_rec *cmd) {
752   const char *tabinfo;
753   int xerrno;
754 
755   if (auth_otp_engine == FALSE) {
756     return PR_DECLINED(cmd);
757   }
758 
759   tabinfo = auth_otp_db_config->argv[0];
760 
761   PRIVS_ROOT
762   dbh = auth_otp_db_open(auth_otp_pool, tabinfo);
763   xerrno = errno;
764   PRIVS_RELINQUISH
765 
766   if (dbh == NULL) {
767     pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
768       "unable to open AuthOTPTable: %s", strerror(xerrno));
769   }
770 
771   return PR_DECLINED(cmd);
772 }
773 
auth_otp_post_user(cmd_rec * cmd)774 MODRET auth_otp_post_user(cmd_rec *cmd) {
775   const char *user;
776 
777   if (auth_otp_engine == FALSE ||
778       dbh == NULL) {
779     return PR_DECLINED(cmd);
780   }
781 
782   user = cmd->argv[1];
783 
784   /* We want to respond using the normal 331 response code, BUT we'd like
785    * to change the response message.  Note that this might be considered
786    * an information leak, i.e. we're leaking the server's expectation of
787    * OTPs from the connecting client.
788    */
789 
790   if (!(auth_otp_opts & AUTH_OTP_OPT_STANDARD_RESPONSE)) {
791     pr_response_clear(&resp_list);
792 #if defined(HAVE_SFTP)
793     /* Note: for some reason, when building with mod_sftp, the '_' function
794      * used for localization is not resolvable by the linker, thus we
795      * work around the problem.  For now.
796      */
797     pr_response_add(R_331, "One-time password required for %s", user);
798 #else
799     pr_response_add(R_331, _("One-time password required for %s"), user);
800 #endif /* HAVE_SFTP */
801   }
802 
803   return PR_DECLINED(cmd);
804 }
805 
806 /* Event listeners
807  */
808 
auth_otp_exit_ev(const void * event_data,void * user_data)809 static void auth_otp_exit_ev(const void *event_data, void *user_data) {
810   if (dbh != NULL) {
811     (void) auth_otp_db_close(dbh);
812     dbh = NULL;
813   }
814 }
815 
816 #if defined(PR_SHARED_MODULE)
auth_otp_mod_unload_ev(const void * event_data,void * user_data)817 static void auth_otp_mod_unload_ev(const void *event_data, void *user_data) {
818   if (strcmp("mod_auth_otp.c", (const char *) event_data) == 0) {
819 # if defined(HAVE_SFTP)
820     if (pr_module_exists("mod_sftp.c") == TRUE) {
821       sftp_kbdint_unregister_driver("auth_otp");
822     }
823 # endif /* HAVE_SFTP */
824     pr_event_unregister(&auth_otp_module, NULL, NULL);
825   }
826 }
827 #endif /* PR_SHARED_MODULE */
828 
auth_otp_sess_reinit_ev(const void * event_data,void * user_data)829 static void auth_otp_sess_reinit_ev(const void *event_data, void *user_data) {
830   int res;
831 
832   /* A HOST command changed the main_server pointer; reinitialize ourselves. */
833 
834   pr_event_unregister(&auth_otp_module, "core.exit", auth_otp_exit_ev);
835   pr_event_unregister(&auth_otp_module, "core.session-reinit",
836     auth_otp_sess_reinit_ev);
837 
838   auth_otp_engine = FALSE;
839   auth_otp_opts = 0UL;
840   auth_otp_algo = AUTH_OTP_ALGO_TOTP_SHA1;
841   auth_otp_db_config = NULL;
842 
843   if (auth_otp_logfd >= 0) {
844     (void) close(auth_otp_logfd);
845     auth_otp_logfd = -1;
846   }
847 
848 #if defined(HAVE_SFTP)
849   auth_otp_using_sftp = FALSE;
850   (void) sftp_kbdint_register_driver("auth_otp", &auth_otp_kbdint_driver);
851 #endif /* HAVE_SFTP */
852 
853   if (auth_otp_pool != NULL) {
854     destroy_pool(auth_otp_pool);
855   }
856 
857   res = auth_otp_sess_init();
858   if (res < 0) {
859     pr_session_disconnect(&auth_otp_module,
860       PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
861   }
862 }
863 
864 /* Initialization routines
865  */
866 
auth_otp_init(void)867 static int auth_otp_init(void) {
868 
869 #if defined(PR_SHARED_MODULE)
870   pr_event_register(&auth_otp_module, "core.module-unload",
871     auth_otp_mod_unload_ev, NULL);
872 #endif /* PR_SHARED_MODULE */
873 
874   if (pr_module_exists("mod_sql.c") == FALSE) {
875     pr_log_pri(PR_LOG_NOTICE, MOD_AUTH_OTP_VERSION
876       ": Missing required 'mod_sql.c'; HOTP/TOTP logins will FAIL");
877   }
878 
879 #if defined(HAVE_SFTP)
880   auth_otp_using_sftp = pr_module_exists("mod_sftp.c");
881   if (auth_otp_using_sftp) {
882     /* Prepare our keyboard-interactive driver. */
883     memset(&auth_otp_kbdint_driver, 0, sizeof(auth_otp_kbdint_driver));
884     auth_otp_kbdint_driver.open = auth_otp_kbdint_open;
885     auth_otp_kbdint_driver.authenticate = auth_otp_kbdint_authenticate;
886     auth_otp_kbdint_driver.close = auth_otp_kbdint_close;
887 
888     if (sftp_kbdint_register_driver("auth_otp", &auth_otp_kbdint_driver) < 0) {
889       int xerrno = errno;
890 
891       pr_log_pri(PR_LOG_NOTICE, MOD_AUTH_OTP_VERSION
892         ": notice: error registering 'keyboard-interactive' driver: %s",
893         strerror(xerrno));
894 
895       errno = xerrno;
896       return -1;
897     }
898 
899   } else {
900     pr_log_debug(DEBUG1, MOD_AUTH_OTP_VERSION
901       ": mod_sftp not loaded, skipping keyboard-interactive support");
902   }
903 #endif /* HAVE_SFTP */
904 
905   return 0;
906 }
907 
auth_otp_sess_init(void)908 static int auth_otp_sess_init(void) {
909   config_rec *c;
910 
911   pr_event_register(&auth_otp_module, "core.session-reinit",
912     auth_otp_sess_reinit_ev, NULL);
913 
914   if (pr_auth_add_auth_only_module("mod_auth_otp.c") < 0 &&
915       errno != EEXIST) {
916     pr_log_pri(PR_LOG_NOTICE, MOD_AUTH_OTP_VERSION
917       ": unable to add 'mod_auth_otp.c' as an auth-only module: %s",
918       strerror(errno));
919 
920     errno = EPERM;
921     return -1;
922   }
923 
924   /* XXX Can we handle both FTP and SSH2 connections in the same module? */
925 
926   c = find_config(main_server->conf, CONF_PARAM, "AuthOTPEngine", FALSE);
927   if (c != NULL) {
928     auth_otp_engine = *((int *) c->argv[0]);
929   }
930 
931   if (auth_otp_engine == FALSE) {
932 #if defined(HAVE_SFTP)
933     if (auth_otp_using_sftp) {
934       sftp_kbdint_unregister_driver("auth_otp");
935     }
936 #endif /* HAVE_SFTP */
937     return 0;
938   }
939 
940   c = find_config(main_server->conf, CONF_PARAM, "AuthOTPLog", FALSE);
941   if (c != NULL) {
942     char *path;
943 
944     path = c->argv[0];
945     if (strncasecmp(path, "none", 5) != 0) {
946       int res, xerrno;
947 
948       pr_signals_block();
949       PRIVS_ROOT
950       res = pr_log_openfile(path, &auth_otp_logfd, 0600);
951       xerrno = errno;
952       PRIVS_RELINQUISH
953       pr_signals_unblock();
954 
955       if (res < 0) {
956         if (res == -1) {
957           pr_log_pri(PR_LOG_NOTICE, MOD_AUTH_OTP_VERSION
958             ": notice: unable to open AuthOTPLog '%s': %s", path,
959             strerror(xerrno));
960 
961         } else if (res == PR_LOG_WRITABLE_DIR) {
962           pr_log_pri(PR_LOG_WARNING, MOD_AUTH_OTP_VERSION
963             ": notice: unable to open AuthOTPLog '%s': parent directory is "
964             "world-writable", path);
965 
966         } else if (res == PR_LOG_SYMLINK) {
967           pr_log_pri(PR_LOG_WARNING, MOD_AUTH_OTP_VERSION
968             ": notice: unable to open AuthOTPLog '%s': cannot log to a symlink",
969             path);
970         }
971       }
972     }
973   }
974 
975   c = find_config(main_server->conf, CONF_PARAM, "AuthOTPTable", FALSE);
976   if (c == NULL) {
977     pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
978       "missing required AuthOTPTable directive, disabling module");
979     pr_log_pri(PR_LOG_NOTICE, MOD_AUTH_OTP_VERSION
980       ": missing required AuthOTPTable directive, disabling module");
981     auth_otp_engine = FALSE;
982     (void) close(auth_otp_logfd);
983     auth_otp_logfd = -1;
984 
985 #if defined(HAVE_SFTP)
986     if (auth_otp_using_sftp) {
987       sftp_kbdint_unregister_driver("auth_otp");
988     }
989 #endif /* HAVE_SFTP */
990 
991     return 0;
992   }
993   auth_otp_db_config = c;
994 
995   auth_otp_pool = make_sub_pool(session.pool);
996   pr_pool_tag(auth_otp_pool, MOD_AUTH_OTP_VERSION);
997 
998   c = find_config(main_server->conf, CONF_PARAM, "AuthOTPAlgorithm", FALSE);
999   if (c != NULL) {
1000     auth_otp_algo = *((int *) c->argv[0]);
1001   }
1002 
1003   c = find_config(main_server->conf, CONF_PARAM, "AuthOTPOptions", FALSE);
1004   while (c != NULL) {
1005     unsigned long opts = 0;
1006 
1007     pr_signals_handle();
1008 
1009     opts = *((unsigned long *) c->argv[0]);
1010     auth_otp_opts |= opts;
1011 
1012     c = find_config_next(c, c->next, CONF_PARAM, "AuthOTPOptions", FALSE);
1013   }
1014 
1015   pr_event_register(&auth_otp_module, "core.exit", auth_otp_exit_ev, NULL);
1016   return 0;
1017 }
1018 
1019 static cmdtable auth_otp_cmdtab[] = {
1020   { PRE_CMD,		C_USER, G_NONE,	auth_otp_pre_user,	FALSE, FALSE },
1021   { POST_CMD,		C_USER, G_NONE,	auth_otp_post_user,	FALSE, FALSE },
1022   { POST_CMD,		C_PASS, G_NONE,	auth_otp_post_pass,	FALSE, FALSE },
1023   { POST_CMD_ERR,	C_PASS, G_NONE,	auth_otp_post_pass,	FALSE, FALSE },
1024   { 0, NULL },
1025 };
1026 
1027 static authtable auth_otp_authtab[] = {
1028   { 0, "auth",	auth_otp_auth },
1029   { 0, "check",	auth_otp_chkpass },
1030   { 0, NULL, NULL }
1031 };
1032 
1033 static conftable auth_otp_conftab[] = {
1034   { "AuthOTPAlgorithm",		set_authotpalgo,		NULL },
1035   { "AuthOTPEngine",		set_authotpengine,		NULL },
1036   { "AuthOTPLog",		set_authotplog,			NULL },
1037   { "AuthOTPOptions",		set_authotpoptions,		NULL },
1038   { "AuthOTPTable",		set_authotptable,		NULL },
1039   { "AuthOTPTableLock",		set_authotptablelock,		NULL },
1040   { NULL, NULL, NULL }
1041 };
1042 
1043 module auth_otp_module = {
1044   NULL, NULL,
1045 
1046   /* Module API version */
1047   0x20,
1048 
1049   /* Module name */
1050   "auth_otp",
1051 
1052   /* Module configuration handler table */
1053   auth_otp_conftab,
1054 
1055   /* Module command handler table */
1056   auth_otp_cmdtab,
1057 
1058   /* Module authentication handler table */
1059   auth_otp_authtab,
1060 
1061   /* Module initialization */
1062   auth_otp_init,
1063 
1064   /* Session initialization */
1065   auth_otp_sess_init,
1066 
1067   /* Module version */
1068   MOD_AUTH_OTP_VERSION
1069 };
1070