auth.c revision 92559
1251876Speter/* 2251876Speter * Copyright (c) 2000 Markus Friedl. All rights reserved. 3251876Speter * 4251876Speter * Redistribution and use in source and binary forms, with or without 5251876Speter * modification, are permitted provided that the following conditions 6251876Speter * are met: 7251876Speter * 1. Redistributions of source code must retain the above copyright 8251876Speter * notice, this list of conditions and the following disclaimer. 9251876Speter * 2. Redistributions in binary form must reproduce the above copyright 10251876Speter * notice, this list of conditions and the following disclaimer in the 11251876Speter * documentation and/or other materials provided with the distribution. 12251876Speter * 13251876Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14251876Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15251876Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16251876Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17251876Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18251876Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19251876Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20251876Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21251876Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22251876Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23251876Speter */ 24251876Speter 25251876Speter#include "includes.h" 26251876SpeterRCSID("$OpenBSD: auth.c,v 1.35 2002/03/01 13:12:10 markus Exp $"); 27251876SpeterRCSID("$FreeBSD: head/crypto/openssh/auth.c 92559 2002-03-18 10:09:43Z des $"); 28251876Speter 29251876Speter#include <libgen.h> 30251876Speter 31251876Speter#include "xmalloc.h" 32251876Speter#include "match.h" 33251876Speter#include "groupaccess.h" 34251876Speter#include "log.h" 35251876Speter#include "servconf.h" 36251876Speter#include "auth.h" 37251876Speter#include "auth-options.h" 38251876Speter#include "canohost.h" 39251876Speter#include "buffer.h" 40251876Speter#include "bufaux.h" 41251876Speter#include "uidswap.h" 42251876Speter#include "tildexpand.h" 43251876Speter 44251876Speter/* import */ 45251876Speterextern ServerOptions options; 46251876Speter 47251876Speter/* 48251876Speter * Check if the user is allowed to log in via ssh. If user is listed 49251876Speter * in DenyUsers or one of user's groups is listed in DenyGroups, false 50251876Speter * will be returned. If AllowUsers isn't empty and user isn't listed 51251876Speter * there, or if AllowGroups isn't empty and one of user's groups isn't 52251876Speter * listed there, false will be returned. 53251876Speter * If the user's shell is not executable, false will be returned. 54251876Speter * Otherwise true is returned. 55251876Speter */ 56251876Speterint 57251876Speterallowed_user(struct passwd * pw) 58251876Speter{ 59251876Speter struct stat st; 60251876Speter const char *hostname = NULL, *ipaddr = NULL; 61251876Speter char *shell; 62251876Speter int i; 63251876Speter 64251876Speter /* Shouldn't be called if pw is NULL, but better safe than sorry... */ 65251876Speter if (!pw || !pw->pw_name) 66251876Speter return 0; 67251876Speter 68251876Speter /* 69251876Speter * Get the shell from the password data. An empty shell field is 70251876Speter * legal, and means /bin/sh. 71251876Speter */ 72251876Speter shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; 73251876Speter 74251876Speter /* deny if shell does not exists or is not executable */ 75251876Speter if (stat(shell, &st) != 0) { 76251876Speter log("User %.100s not allowed because shell %.100s does not exist", 77251876Speter pw->pw_name, shell); 78251876Speter return 0; 79251876Speter } 80251876Speter if (!((st.st_mode & S_IFREG) && (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)))) { 81251876Speter log("User %.100s not allowed because shell %.100s is not executable", 82251876Speter pw->pw_name, shell); 83251876Speter return 0; 84251876Speter } 85251876Speter 86251876Speter if (options.num_deny_users > 0 || options.num_allow_users > 0) { 87251876Speter hostname = get_canonical_hostname(options.verify_reverse_mapping); 88251876Speter ipaddr = get_remote_ipaddr(); 89251876Speter } 90251876Speter 91251876Speter /* Return false if user is listed in DenyUsers */ 92251876Speter if (options.num_deny_users > 0) { 93251876Speter for (i = 0; i < options.num_deny_users; i++) 94251876Speter if (match_user(pw->pw_name, hostname, ipaddr, 95251876Speter options.deny_users[i])) { 96251876Speter log("User %.100s not allowed because listed in DenyUsers", 97251876Speter pw->pw_name); 98251876Speter return 0; 99251876Speter } 100251876Speter } 101251876Speter /* Return false if AllowUsers isn't empty and user isn't listed there */ 102251876Speter if (options.num_allow_users > 0) { 103251876Speter for (i = 0; i < options.num_allow_users; i++) 104251876Speter if (match_user(pw->pw_name, hostname, ipaddr, 105251876Speter options.allow_users[i])) 106251876Speter break; 107251876Speter /* i < options.num_allow_users iff we break for loop */ 108251876Speter if (i >= options.num_allow_users) { 109251876Speter log("User %.100s not allowed because not listed in AllowUsers", 110251876Speter pw->pw_name); 111251876Speter return 0; 112251876Speter } 113251876Speter } 114251876Speter if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { 115251876Speter /* Get the user's group access list (primary and supplementary) */ 116251876Speter if (ga_init(pw->pw_name, pw->pw_gid) == 0) { 117251876Speter log("User %.100s not allowed because not in any group", 118251876Speter pw->pw_name); 119251876Speter return 0; 120251876Speter } 121251876Speter 122251876Speter /* Return false if one of user's groups is listed in DenyGroups */ 123251876Speter if (options.num_deny_groups > 0) 124251876Speter if (ga_match(options.deny_groups, 125251876Speter options.num_deny_groups)) { 126251876Speter ga_free(); 127251876Speter log("User %.100s not allowed because a group is listed in DenyGroups", 128251876Speter pw->pw_name); 129251876Speter return 0; 130251876Speter } 131251876Speter /* 132251876Speter * Return false if AllowGroups isn't empty and one of user's groups 133251876Speter * isn't listed there 134251876Speter */ 135251876Speter if (options.num_allow_groups > 0) 136251876Speter if (!ga_match(options.allow_groups, 137251876Speter options.num_allow_groups)) { 138251876Speter ga_free(); 139251876Speter log("User %.100s not allowed because none of user's groups are listed in AllowGroups", 140251876Speter pw->pw_name); 141251876Speter return 0; 142251876Speter } 143251876Speter ga_free(); 144251876Speter } 145251876Speter /* We found no reason not to let this user try to log on... */ 146 return 1; 147} 148 149Authctxt * 150authctxt_new(void) 151{ 152 Authctxt *authctxt = xmalloc(sizeof(*authctxt)); 153 memset(authctxt, 0, sizeof(*authctxt)); 154 return authctxt; 155} 156 157void 158auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) 159{ 160 void (*authlog) (const char *fmt,...) = verbose; 161 char *authmsg; 162 163 /* Raise logging level */ 164 if (authenticated == 1 || 165 !authctxt->valid || 166 authctxt->failures >= AUTH_FAIL_LOG || 167 strcmp(method, "password") == 0) 168 authlog = log; 169 170 if (authctxt->postponed) 171 authmsg = "Postponed"; 172 else 173 authmsg = authenticated ? "Accepted" : "Failed"; 174 175 authlog("%s %s for %s%.100s from %.200s port %d%s", 176 authmsg, 177 method, 178 authctxt->valid ? "" : "illegal user ", 179 authctxt->user, 180 get_remote_ipaddr(), 181 get_remote_port(), 182 info); 183} 184 185/* 186 * Check whether root logins are disallowed. 187 */ 188int 189auth_root_allowed(char *method) 190{ 191 switch (options.permit_root_login) { 192 case PERMIT_YES: 193 return 1; 194 break; 195 case PERMIT_NO_PASSWD: 196 if (strcmp(method, "password") != 0) 197 return 1; 198 break; 199 case PERMIT_FORCED_ONLY: 200 if (forced_command) { 201 log("Root login accepted for forced command."); 202 return 1; 203 } 204 break; 205 } 206 log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); 207 return 0; 208} 209 210 211/* 212 * Given a template and a passwd structure, build a filename 213 * by substituting % tokenised options. Currently, %% becomes '%', 214 * %h becomes the home directory and %u the username. 215 * 216 * This returns a buffer allocated by xmalloc. 217 */ 218char * 219expand_filename(const char *filename, struct passwd *pw) 220{ 221 Buffer buffer; 222 char *file; 223 const char *cp; 224 225 /* 226 * Build the filename string in the buffer by making the appropriate 227 * substitutions to the given file name. 228 */ 229 buffer_init(&buffer); 230 for (cp = filename; *cp; cp++) { 231 if (cp[0] == '%' && cp[1] == '%') { 232 buffer_append(&buffer, "%", 1); 233 cp++; 234 continue; 235 } 236 if (cp[0] == '%' && cp[1] == 'h') { 237 buffer_append(&buffer, pw->pw_dir, strlen(pw->pw_dir)); 238 cp++; 239 continue; 240 } 241 if (cp[0] == '%' && cp[1] == 'u') { 242 buffer_append(&buffer, pw->pw_name, 243 strlen(pw->pw_name)); 244 cp++; 245 continue; 246 } 247 buffer_append(&buffer, cp, 1); 248 } 249 buffer_append(&buffer, "\0", 1); 250 251 /* 252 * Ensure that filename starts anchored. If not, be backward 253 * compatible and prepend the '%h/' 254 */ 255 file = xmalloc(MAXPATHLEN); 256 cp = buffer_ptr(&buffer); 257 if (*cp != '/') 258 snprintf(file, MAXPATHLEN, "%s/%s", pw->pw_dir, cp); 259 else 260 strlcpy(file, cp, MAXPATHLEN); 261 262 buffer_free(&buffer); 263 return file; 264} 265 266char * 267authorized_keys_file(struct passwd *pw) 268{ 269 return expand_filename(options.authorized_keys_file, pw); 270} 271 272char * 273authorized_keys_file2(struct passwd *pw) 274{ 275 return expand_filename(options.authorized_keys_file2, pw); 276} 277 278/* return ok if key exists in sysfile or userfile */ 279HostStatus 280check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, 281 const char *sysfile, const char *userfile) 282{ 283 Key *found; 284 char *user_hostfile; 285 struct stat st; 286 HostStatus host_status; 287 288 /* Check if we know the host and its host key. */ 289 found = key_new(key->type); 290 host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); 291 292 if (host_status != HOST_OK && userfile != NULL) { 293 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 294 if (options.strict_modes && 295 (stat(user_hostfile, &st) == 0) && 296 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 297 (st.st_mode & 022) != 0)) { 298 log("Authentication refused for %.100s: " 299 "bad owner or modes for %.200s", 300 pw->pw_name, user_hostfile); 301 } else { 302 temporarily_use_uid(pw); 303 host_status = check_host_in_hostfile(user_hostfile, 304 host, key, found, NULL); 305 restore_uid(); 306 } 307 xfree(user_hostfile); 308 } 309 key_free(found); 310 311 debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ? 312 "ok" : "not found", host); 313 return host_status; 314} 315 316 317/* 318 * Check a given file for security. This is defined as all components 319 * of the path to the file must either be owned by either the owner of 320 * of the file or root and no directories must be group or world writable. 321 * 322 * XXX Should any specific check be done for sym links ? 323 * 324 * Takes an open file descriptor, the file name, a uid and and 325 * error buffer plus max size as arguments. 326 * 327 * Returns 0 on success and -1 on failure 328 */ 329int 330secure_filename(FILE *f, const char *file, struct passwd *pw, 331 char *err, size_t errlen) 332{ 333 uid_t uid = pw->pw_uid; 334 char buf[MAXPATHLEN], homedir[MAXPATHLEN]; 335 char *cp; 336 struct stat st; 337 338 if (realpath(file, buf) == NULL) { 339 snprintf(err, errlen, "realpath %s failed: %s", file, 340 strerror(errno)); 341 return -1; 342 } 343 if (realpath(pw->pw_dir, homedir) == NULL) { 344 snprintf(err, errlen, "realpath %s failed: %s", pw->pw_dir, 345 strerror(errno)); 346 return -1; 347 } 348 349 /* check the open file to avoid races */ 350 if (fstat(fileno(f), &st) < 0 || 351 (st.st_uid != 0 && st.st_uid != uid) || 352 (st.st_mode & 022) != 0) { 353 snprintf(err, errlen, "bad ownership or modes for file %s", 354 buf); 355 return -1; 356 } 357 358 /* for each component of the canonical path, walking upwards */ 359 for (;;) { 360 if ((cp = dirname(buf)) == NULL) { 361 snprintf(err, errlen, "dirname() failed"); 362 return -1; 363 } 364 strlcpy(buf, cp, sizeof(buf)); 365 366 debug3("secure_filename: checking '%s'", buf); 367 if (stat(buf, &st) < 0 || 368 (st.st_uid != 0 && st.st_uid != uid) || 369 (st.st_mode & 022) != 0) { 370 snprintf(err, errlen, 371 "bad ownership or modes for directory %s", buf); 372 return -1; 373 } 374 375 /* If are passed the homedir then we can stop */ 376 if (strcmp(homedir, buf) == 0) { 377 debug3("secure_filename: terminating check at '%s'", 378 buf); 379 break; 380 } 381 /* 382 * dirname should always complete with a "/" path, 383 * but we can be paranoid and check for "." too 384 */ 385 if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 386 break; 387 } 388 return 0; 389} 390