2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by Markus Friedl.
15 * 4. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 RCSID("$OpenBSD: auth2.c,v 1.13 2000/08/20 18:42:40 millert Exp $");
32 #include <openssl/dsa.h>
33 #include <openssl/rsa.h>
34 #include <openssl/evp.h>
57 #include "auth-options.h"
65 extern ServerOptions options;
66 extern unsigned char *session_id2;
67 extern int session_id2_len;
71 void input_service_request(int type, int plen);
72 void input_userauth_request(int type, int plen);
73 void protocol_error(int type, int plen);
76 int ssh2_auth_none(struct passwd *pw);
77 int ssh2_auth_password(struct passwd *pw);
78 int ssh2_auth_pubkey(struct passwd *pw, char *service);
81 struct passwd* auth_set_user(char *u, char *s);
82 int user_dsa_key_allowed(struct passwd *pw, Key *key);
84 typedef struct Authctxt Authctxt;
91 static Authctxt *authctxt = NULL;
92 static int userauth_success = 0;
95 * loop until userauth_success == TRUE
101 /* turn off skey/kerberos, not supported by SSH2 */
103 options.skey_authentication = 0;
106 options.kerberos_authentication = 0;
109 dispatch_init(&protocol_error);
110 dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
111 dispatch_run(DISPATCH_BLOCK, &userauth_success);
116 protocol_error(int type, int plen)
118 log("auth: protocol error: type %d plen %d", type, plen);
119 packet_start(SSH2_MSG_UNIMPLEMENTED);
126 input_service_request(int type, int plen)
130 char *service = packet_get_string(&len);
133 if (strcmp(service, "ssh-userauth") == 0) {
134 if (!userauth_success) {
136 /* now we can handle user-auth requests */
137 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
140 /* XXX all other service requests are denied */
143 packet_start(SSH2_MSG_SERVICE_ACCEPT);
144 packet_put_cstring(service);
148 debug("bad service request %s", service);
149 packet_disconnect("bad service request %s", service);
155 input_userauth_request(int type, int plen)
157 static void (*authlog) (const char *fmt,...) = verbose;
158 static int attempt = 0;
160 int authenticated = 0;
161 char *user, *service, *method, *authmsg = NULL;
163 #ifdef WITH_AIXAUTHENTICATE
164 extern char *aixloginmsg;
165 #endif /* WITH_AIXAUTHENTICATE */
167 user = packet_get_string(&len);
168 service = packet_get_string(&len);
169 method = packet_get_string(&len);
170 if (++attempt == AUTH_FAIL_MAX) {
171 #ifdef WITH_AIXAUTHENTICATE
172 loginfailed(user,get_canonical_hostname(),"ssh");
173 #endif /* WITH_AIXAUTHENTICATE */
174 packet_disconnect("too many failed userauth_requests");
176 debug("userauth-request for user %s service %s method %s", user, service, method);
178 /* XXX we only allow the ssh-connection service */
179 pw = auth_set_user(user, service);
180 if (pw && strcmp(service, "ssh-connection")==0) {
181 if (strcmp(method, "none") == 0) {
182 authenticated = ssh2_auth_none(pw);
183 } else if (strcmp(method, "password") == 0) {
184 authenticated = ssh2_auth_password(pw);
185 } else if (strcmp(method, "publickey") == 0) {
186 authenticated = ssh2_auth_pubkey(pw, service);
189 if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) {
191 log("ROOT LOGIN REFUSED FROM %.200s",
192 get_canonical_hostname());
196 if (authenticated && !do_pam_account(pw->pw_name, NULL))
200 /* Raise logging level */
201 if (authenticated == 1 ||
202 attempt == AUTH_FAIL_LOG ||
203 strcmp(method, "password") == 0)
206 /* Log before sending the reply */
207 if (authenticated == 1) {
208 authmsg = "Accepted";
209 } else if (authenticated == 0) {
212 authmsg = "Postponed";
214 authlog("%s %s for %.200s from %.200s port %d ssh2",
217 pw && pw->pw_uid == 0 ? "ROOT" : user,
221 /* XXX todo: check if multiple auth methods are needed */
222 if (authenticated == 1) {
223 #ifdef WITH_AIXAUTHENTICATE
224 /* We don't have a pty yet, so just label the line as "ssh" */
225 if (loginsuccess(user,get_canonical_hostname(),"ssh",
228 #endif /* WITH_AIXAUTHENTICATE */
229 /* turn off userauth */
230 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
231 packet_start(SSH2_MSG_USERAUTH_SUCCESS);
234 /* now we can break out */
235 userauth_success = 1;
236 } else if (authenticated == 0) {
237 packet_start(SSH2_MSG_USERAUTH_FAILURE);
238 packet_put_cstring("publickey,password"); /* XXX dynamic */
239 packet_put_char(0); /* XXX partial success, unused */
250 ssh2_auth_none(struct passwd *pw)
253 extern int saved_argc;
254 extern char **saved_argv;
260 return auth_pam_password(pw, "");
261 #elif defined(HAVE_OSF_SIA)
262 return(sia_validate_user(NULL, saved_argc, saved_argv,
263 get_canonical_hostname(), pw->pw_name, NULL, 0, NULL,
265 #else /* !HAVE_OSF_SIA && !USE_PAM */
266 return auth_password(pw, "");
270 ssh2_auth_password(struct passwd *pw)
273 int authenticated = 0;
277 extern int saved_argc;
278 extern char **saved_argv;
280 change = packet_get_char();
282 log("password change not supported");
283 password = packet_get_string(&len);
285 if (options.password_authentication &&
287 auth_pam_password(pw, password) == 1)
288 #elif defined(HAVE_OSF_SIA)
289 sia_validate_user(NULL, saved_argc, saved_argv,
290 get_canonical_hostname(), pw->pw_name, NULL, 0,
291 NULL, password) == SIASUCCESS)
292 #else /* !USE_PAM && !HAVE_OSF_SIA */
293 auth_password(pw, password) == 1)
296 memset(password, 0, len);
298 return authenticated;
301 ssh2_auth_pubkey(struct passwd *pw, char *service)
305 char *pkalg, *pkblob, *sig;
306 unsigned int alen, blen, slen;
308 int authenticated = 0;
310 if (options.dsa_authentication == 0) {
311 debug("pubkey auth disabled");
314 have_sig = packet_get_char();
315 pkalg = packet_get_string(&alen);
316 if (strcmp(pkalg, KEX_DSS) != 0) {
318 log("bad pkalg %s", pkalg); /*XXX*/
321 pkblob = packet_get_string(&blen);
322 key = dsa_key_from_blob(pkblob, blen);
325 sig = packet_get_string(&slen);
328 if (datafellows & SSH_COMPAT_SESSIONID_ENCODING) {
329 buffer_put_string(&b, session_id2, session_id2_len);
331 buffer_append(&b, session_id2, session_id2_len);
333 /* reconstruct packet */
334 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
335 buffer_put_cstring(&b, pw->pw_name);
336 buffer_put_cstring(&b,
337 datafellows & SSH_BUG_PUBKEYAUTH ?
340 buffer_put_cstring(&b, "publickey");
341 buffer_put_char(&b, have_sig);
342 buffer_put_cstring(&b, KEX_DSS);
343 buffer_put_string(&b, pkblob, blen);
347 /* test for correct signature */
348 if (user_dsa_key_allowed(pw, key) &&
349 dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
355 debug("test key...");
356 /* test whether pkalg/pkblob are acceptable */
357 /* XXX fake reply and always send PK_OK ? */
359 * XXX this allows testing whether a user is allowed
360 * to login: if you happen to have a valid pubkey this
361 * message is sent. the message is NEVER sent at all
362 * if a user is not allowed to login. is this an
365 if (user_dsa_key_allowed(pw, key)) {
366 packet_start(SSH2_MSG_USERAUTH_PK_OK);
367 packet_put_string(pkalg, alen);
368 packet_put_string(pkblob, blen);
378 return authenticated;
381 /* set and get current user */
386 return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
390 auth_set_user(char *u, char *s)
392 struct passwd *pw, *copy;
394 if (authctxt == NULL) {
395 authctxt = xmalloc(sizeof(*authctxt));
397 authctxt->user = xstrdup(u);
398 authctxt->service = xstrdup(s);
399 setproctitle("%s", u);
401 if (!pw || !allowed_user(pw)) {
402 log("auth_set_user: illegal user %s", u);
408 copy = &authctxt->pw;
409 memset(copy, 0, sizeof(*copy));
410 copy->pw_name = xstrdup(pw->pw_name);
411 copy->pw_passwd = xstrdup(pw->pw_passwd);
412 copy->pw_uid = pw->pw_uid;
413 copy->pw_gid = pw->pw_gid;
414 #ifdef HAVE_PW_CLASS_IN_PASSWD
415 copy->pw_class = xstrdup(pw->pw_class);
417 copy->pw_dir = xstrdup(pw->pw_dir);
418 copy->pw_shell = xstrdup(pw->pw_shell);
421 if (strcmp(u, authctxt->user) != 0 ||
422 strcmp(s, authctxt->service) != 0) {
423 log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
424 u, s, authctxt->user, authctxt->service);
428 return auth_get_user();
431 /* return 1 if user allows given key */
433 user_dsa_key_allowed(struct passwd *pw, Key *key)
435 char line[8192], file[1024];
437 unsigned int bits = -1;
439 unsigned long linenum = 0;
443 /* Temporarily use the user's uid. */
444 temporarily_use_uid(pw->pw_uid);
446 /* The authorized keys. */
447 snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
448 SSH_USER_PERMITTED_KEYS2);
450 /* Fail quietly if file does not exist */
451 if (stat(file, &st) < 0) {
452 /* Restore the privileged uid. */
456 /* Open the file containing the authorized keys. */
457 f = fopen(file, "r");
459 /* Restore the privileged uid. */
463 if (options.strict_modes) {
466 /* Check open file in order to avoid open/stat races */
467 if (fstat(fileno(f), &st) < 0 ||
468 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
469 (st.st_mode & 022) != 0) {
470 snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: "
471 "bad ownership or modes for '%s'.", pw->pw_name, file);
474 /* Check path to SSH_USER_PERMITTED_KEYS */
476 static const char *check[] = {
477 "", SSH_USER_DIR, NULL
479 for (i = 0; check[i]; i++) {
480 snprintf(line, sizeof line, "%.500s/%.100s",
481 pw->pw_dir, check[i]);
482 if (stat(line, &st) < 0 ||
483 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
484 (st.st_mode & 022) != 0) {
485 snprintf(buf, sizeof buf,
486 "DSA authentication refused for %.100s: "
487 "bad ownership or modes for '%s'.",
502 found = key_new(KEY_DSA);
504 while (fgets(line, sizeof(line), f)) {
505 char *cp, *options = NULL;
507 /* Skip leading whitespace, empty and comment lines. */
508 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
510 if (!*cp || *cp == '\n' || *cp == '#')
513 bits = key_read(found, &cp);
515 /* no key? check if there are options for this key */
518 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
519 if (*cp == '\\' && cp[1] == '"')
520 cp++; /* Skip both */
524 /* Skip remaining whitespace. */
525 for (; *cp == ' ' || *cp == '\t'; cp++)
527 bits = key_read(found, &cp);
529 /* still no key? advance to next line*/
533 if (key_equal(found, key) &&
534 auth_parse_options(pw, options, linenum) == 1) {
536 debug("matching key found: file %s, line %ld",