1 /*
2  * ProFTPD - mod_sftp 'keyboard-interactive' user authentication
3  * Copyright (c) 2008-2015 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 
25 #include "mod_sftp.h"
26 #include "ssh2.h"
27 #include "packet.h"
28 #include "auth.h"
29 #include "msg.h"
30 #include "cipher.h"
31 #include "mac.h"
32 #include "utf8.h"
33 #include "kbdint.h"
34 
35 /* This array tracks the names of kbdint drivers that have successfully
36  * authenticated the user.  In any given session, the same kbdint driver CANNOT
37  * be used twice for authentication; this supports authentication chains
38  * like "keyboard-interactive+keyboard-interactive", requiring clients to
39  * authenticate using multiple different kbdint means.
40  */
41 static array_header *kbdint_drivers = NULL;
42 
43 static const char *trace_channel = "ssh2";
44 
sftp_auth_kbdint(struct ssh2_packet * pkt,cmd_rec * pass_cmd,const char * orig_user,const char * user,const char * service,unsigned char ** buf,uint32_t * buflen,int * send_userauth_fail)45 int sftp_auth_kbdint(struct ssh2_packet *pkt, cmd_rec *pass_cmd,
46     const char *orig_user, const char *user, const char *service,
47     unsigned char **buf, uint32_t *buflen, int *send_userauth_fail) {
48   const char *cipher_algo, *mac_algo;
49   struct passwd *pw;
50   char *submethods;
51   sftp_kbdint_driver_t *driver;
52   int res = -1;
53 
54   if (sftp_kbdint_have_drivers() == 0) {
55     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
56       "no 'keyboard-interactive' drivers currently registered, unable to "
57       "authenticate user '%s' via 'keyboard-interactive' method", user);
58 
59     pr_log_auth(PR_LOG_NOTICE,
60       "USER %s (Login failed): keyboard-interactive authentication disabled",
61       user);
62 
63     *send_userauth_fail = TRUE;
64     errno = EPERM;
65     return 0;
66   }
67 
68   if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) {
69     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
70       "authentication request for user '%s' blocked by '%s' handler",
71       orig_user, (char *) pass_cmd->argv[0]);
72 
73     pr_log_auth(PR_LOG_NOTICE,
74       "USER %s (Login failed): blocked by '%s' handler", orig_user,
75       (char *) pass_cmd->argv[0]);
76 
77     pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0);
78     pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0);
79 
80     *send_userauth_fail = TRUE;
81     errno = EPERM;
82     return 0;
83   }
84 
85   pw = pr_auth_getpwnam(pkt->pool, user);
86   if (pw == NULL) {
87     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
88       "no account for user '%s' found", user);
89 
90     pr_log_auth(PR_LOG_NOTICE,
91       "USER %s: no such user found from %s [%s] to %s:%d", user,
92       session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr),
93       pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port);
94 
95     *send_userauth_fail = TRUE;
96     errno = ENOENT;
97     return 0;
98   }
99 
100   cipher_algo = sftp_cipher_get_read_algo();
101   mac_algo = sftp_mac_get_read_algo();
102 
103   /* XXX Is this too strict?  For PAM authentication, no -- but for S/Key or
104    * one-time password authencation, maybe yes.
105    */
106   if (strncmp(cipher_algo, "none", 5) == 0 ||
107       strncmp(mac_algo, "none", 5) == 0) {
108 
109     if (sftp_opts & SFTP_OPT_ALLOW_INSECURE_LOGIN) {
110       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
111         "WARNING: cipher algorithm '%s' or MAC algorithm '%s' INSECURE for "
112         "keyboard-interactive authentication "
113         "(SFTPOption AllowInsecureLogin in effect)", cipher_algo, mac_algo);
114 
115     } else {
116       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
117         "cipher algorithm '%s' or MAC algorithm '%s' unacceptable for "
118         "keyboard-interactive authentication, denying authentication request",
119         cipher_algo, mac_algo);
120 
121       pr_log_auth(PR_LOG_NOTICE,
122         "USER %s (Login failed): cipher algorithm '%s' or MAC algorithm '%s' "
123         "unsupported for keyboard-interactive authentication", user,
124         cipher_algo, mac_algo);
125 
126       *send_userauth_fail = TRUE;
127       errno = EPERM;
128       return 0;
129     }
130   }
131 
132   /* XXX Read off the deprecated language string. */
133   sftp_msg_read_string(pkt->pool, buf, buflen);
134 
135   submethods = sftp_msg_read_string(pkt->pool, buf, buflen);
136 
137   if (strlen(submethods) > 0) {
138     pr_trace_msg(trace_channel, 8, "client suggested 'keyboard-interactive' "
139       "methods: %s", submethods);
140   }
141 
142   /* XXX get our own get_shared_name() function (see kex.c), to see if
143    * any of the "hints" sent by the client match any of the registered
144    * kbdint drivers.
145    */
146 
147   driver = sftp_kbdint_first_driver();
148   while (driver != NULL) {
149     register unsigned int i;
150     int skip_driver = FALSE;
151 
152     pr_signals_handle();
153 
154     /* If this driver has already successfully handled this user, skip it. */
155     for (i = 0; i < kbdint_drivers->nelts; i++) {
156       char *dri;
157 
158       dri = ((char **) kbdint_drivers->elts)[i];
159       if (strcmp(driver->driver_name, dri) == 0) {
160         skip_driver = TRUE;
161         break;
162       }
163     }
164 
165     if (skip_driver) {
166       pr_trace_msg(trace_channel, 9,
167         "skipping already-used kbdint driver '%s' for user '%s'",
168         driver->driver_name, user);
169       driver = sftp_kbdint_next_driver();
170       continue;
171     }
172 
173     pr_trace_msg(trace_channel, 3, "trying kbdint driver '%s' for user '%s'",
174       driver->driver_name, user);
175 
176     res = driver->open(driver, user);
177     if (res < 0) {
178       driver = sftp_kbdint_next_driver();
179       continue;
180     }
181 
182     res = driver->authenticate(driver, user);
183     driver->close(driver);
184 
185     if (res == 0) {
186       /* Store the driver name for future checking. */
187       *((char **) push_array(kbdint_drivers)) = pstrdup(sftp_pool,
188         driver->driver_name);
189       break;
190     }
191 
192     driver = sftp_kbdint_next_driver();
193   }
194 
195   if (res < 0) {
196     *send_userauth_fail = TRUE;
197 
198     /* We explicitly want to use an errno value other than EPERM here, so
199      * that the calling code allows the connecting client to make other
200      * login attempts, rather than failing this authentication method
201      * after a single failure (Bug#3921).
202      */
203     errno = EACCES;
204     return 0;
205   }
206 
207   return 1;
208 }
209 
sftp_auth_kbdint_init(pool * p)210 int sftp_auth_kbdint_init(pool *p) {
211   kbdint_drivers = make_array(p, 0, sizeof(char *));
212 
213   return 0;
214 }
215