1/* $NetBSD: auth-passwd.c,v 1.13 2023/07/07 07:04:17 rin Exp $ */ 2/* $OpenBSD: auth-passwd.c,v 1.48 2020/10/18 11:32:01 djm Exp $ */ 3 4/* 5 * Author: Tatu Ylonen <ylo@cs.hut.fi> 6 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 7 * All rights reserved 8 * Password authentication. This file contains the functions to check whether 9 * the password is valid for the user. 10 * 11 * As far as I am concerned, the code I have written for this software 12 * can be used freely for any purpose. Any derived versions of this 13 * software must be clearly marked as such, and if the derived work is 14 * incompatible with the protocol description in the RFC file, it must be 15 * called by a name other than "ssh" or "Secure Shell". 16 * 17 * Copyright (c) 1999 Dug Song. All rights reserved. 18 * Copyright (c) 2000 Markus Friedl. All rights reserved. 19 * 20 * Redistribution and use in source and binary forms, with or without 21 * modification, are permitted provided that the following conditions 22 * are met: 23 * 1. Redistributions of source code must retain the above copyright 24 * notice, this list of conditions and the following disclaimer. 25 * 2. Redistributions in binary form must reproduce the above copyright 26 * notice, this list of conditions and the following disclaimer in the 27 * documentation and/or other materials provided with the distribution. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 30 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 31 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 32 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 33 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 34 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 38 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41#include "includes.h" 42__RCSID("$NetBSD: auth-passwd.c,v 1.13 2023/07/07 07:04:17 rin Exp $"); 43#include <sys/types.h> 44 45#include <login_cap.h> 46#include <pwd.h> 47#include <stdio.h> 48#include <string.h> 49#include <stdarg.h> 50#include <unistd.h> 51 52#include "packet.h" 53#include "sshbuf.h" 54#include "ssherr.h" 55#include "log.h" 56#include "misc.h" 57#include "servconf.h" 58#include "sshkey.h" 59#include "hostfile.h" 60#include "auth.h" 61#include "auth-options.h" 62 63extern struct sshbuf *loginmsg; 64extern ServerOptions options; 65int sys_auth_passwd(struct ssh *, const char *); 66 67#ifdef HAVE_LOGIN_CAP 68extern login_cap_t *lc; 69#endif 70 71#define DAY (24L * 60 * 60) /* 1 day in seconds */ 72#define TWO_WEEKS (2L * 7 * DAY) /* 2 weeks in seconds */ 73 74#define MAX_PASSWORD_LEN 1024 75 76/* 77 * Tries to authenticate the user using password. Returns true if 78 * authentication succeeds. 79 */ 80int 81auth_password(struct ssh *ssh, const char *password) 82{ 83 Authctxt *authctxt = ssh->authctxt; 84 struct passwd *pw = authctxt->pw; 85 int ok = authctxt->valid; 86 87 if (strlen(password) > MAX_PASSWORD_LEN) 88 return 0; 89 90 if (pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES) 91 ok = 0; 92 if (*password == '\0' && options.permit_empty_passwd == 0) 93 return 0; 94#ifdef KRB5 95 if (options.kerberos_authentication == 1) { 96 int ret = auth_krb5_password(authctxt, password); 97 if (ret == 1 || ret == 0) 98 return ret && ok; 99 /* Fall back to ordinary passwd authentication. */ 100 } 101#endif 102 103#ifdef USE_PAM 104 if (options.use_pam) 105 return (sshpam_auth_passwd(authctxt, password) && ok); 106#endif 107 return (sys_auth_passwd(ssh, password) && ok); 108} 109 110#ifdef BSD_AUTH 111static void 112warn_expiry(Authctxt *authctxt, auth_session_t *as) 113{ 114 int r; 115 quad_t pwtimeleft, actimeleft, daysleft, pwwarntime, acwarntime; 116 117 pwwarntime = acwarntime = TWO_WEEKS; 118 119 pwtimeleft = auth_check_change(as); 120 actimeleft = auth_check_expire(as); 121#ifdef HAVE_LOGIN_CAP 122 if (authctxt->valid) { 123 pwwarntime = login_getcaptime(lc, "password-warn", TWO_WEEKS, 124 TWO_WEEKS); 125 acwarntime = login_getcaptime(lc, "expire-warn", TWO_WEEKS, 126 TWO_WEEKS); 127 } 128#endif 129 if (pwtimeleft != 0 && pwtimeleft < pwwarntime) { 130 daysleft = pwtimeleft / DAY + 1; 131 if ((r = sshbuf_putf(loginmsg, 132 "Your password will expire in %lld day%s.\n", 133 daysleft, daysleft == 1 ? "" : "s")) != 0) 134 fatal_fr(r, "buffer error"); 135 } 136 if (actimeleft != 0 && actimeleft < acwarntime) { 137 daysleft = actimeleft / DAY + 1; 138 if ((r = sshbuf_putf(loginmsg, 139 "Your account will expire in %lld day%s.\n", 140 daysleft, daysleft == 1 ? "" : "s")) != 0) 141 fatal_fr(r, "buffer error"); 142 } 143} 144 145int 146sys_auth_passwd(struct ssh *ssh, const char *password) 147{ 148 Authctxt *authctxt = ssh->authctxt; 149 auth_session_t *as; 150 static int expire_checked = 0; 151 152 as = auth_usercheck(authctxt->pw->pw_name, authctxt->style, "auth-ssh", 153 (char *)password); 154 if (as == NULL) 155 return (0); 156 if (auth_getstate(as) & AUTH_PWEXPIRED) { 157 auth_close(as); 158 auth_restrict_session(ssh); 159 authctxt->force_pwchange = 1; 160 return (1); 161 } else { 162 if (!expire_checked) { 163 expire_checked = 1; 164 warn_expiry(authctxt, as); 165 } 166 return (auth_close(as)); 167 } 168} 169#else 170 171#define shadow_pw(pw) (pw)->pw_passwd 172#define xcrypt(a, b) crypt((a), (b)) 173 174int 175sys_auth_passwd(struct ssh *ssh, const char *password) 176{ 177 Authctxt *authctxt = ssh->authctxt; 178 struct passwd *pw = authctxt->pw; 179 char *encrypted_password, *salt = NULL; 180 181 /* Just use the supplied fake password if authctxt is invalid */ 182 char *pw_password = authctxt->valid ? shadow_pw(pw) : pw->pw_passwd; 183 184 /* Check for users with no password. */ 185 if (strcmp(pw_password, "") == 0 && strcmp(password, "") == 0) 186 return (1); 187 188 /* 189 * Encrypt the candidate password using the proper salt, or pass a 190 * NULL and let xcrypt pick one. 191 */ 192 if (authctxt->valid && pw_password[0] && pw_password[1]) 193 salt = pw_password; 194 encrypted_password = xcrypt(password, salt ? salt : "xx"); 195 196 /* 197 * Authentication is accepted if the encrypted passwords 198 * are identical. 199 */ 200 return encrypted_password != NULL && 201 strcmp(encrypted_password, pw_password) == 0; 202} 203#endif 204