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