1 /*
2  * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2008, 2009, 2010,
3  *               2011, 2012, 2013, 2017
4  *      Inferno Nettverk A/S, Norway.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. The above copyright notice, this list of conditions and the following
10  *    disclaimer must appear in all copies of the software, derivative works
11  *    or modified versions, and any portions thereof, aswell as in all
12  *    supporting documentation.
13  * 2. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by
16  *      Inferno Nettverk A/S, Norway.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * Inferno Nettverk A/S requests users of this software to return to
32  *
33  *  Software Distribution Coordinator  or  sdc@inet.no
34  *  Inferno Nettverk A/S
35  *  Oslo Research Park
36  *  Gaustadall�en 21
37  *  NO-0349 Oslo
38  *  Norway
39  *
40  * any improvements or extensions that they make and grant Inferno Nettverk A/S
41  * the rights to redistribute these changes.
42  *
43  */
44 
45 #include "common.h"
46 
47 #if HAVE_SHADOW_H && HAVE_GETSPNAM
48 #include <shadow.h>
49 #endif /* HAVE_SHADOW_H && HAVE_GETSPNAM */
50 
51 static const char rcsid[] =
52 "$Id: auth_password.c,v 1.41.6.2 2017/01/31 08:17:38 karls Exp $";
53 
54 static const char *
55 sockd_getpasswordhash(const char *login, char *pw, const size_t pwsize,
56                       char *emsg, const size_t emsglen);
57 /*
58  * Fetches the password hash for the username "login".
59  * The returned hash is stored in "pw", which is of size "pwsize".
60  *
61  * Returns the password hash on success, or NULL on failure.  On failure,
62  * emsg, which must be of size emsglen, contains the reason for the error.
63  */
64 
65 int
passwordcheck(name,cleartextpw,emsg,emsglen)66 passwordcheck(name, cleartextpw, emsg, emsglen)
67    const char *name;
68    const char *cleartextpw;
69    char *emsg;
70    size_t emsglen;
71 {
72    const char *function = "passwordcheck()";
73    const char *p;
74    char visstring[MAXNAMELEN * 4], pwhash[MAXPWLEN],  *crypted;
75    int rc;
76 
77    slog(LOG_DEBUG, "%s: name = %s, password = %s",
78         function,
79          str2vis(name,
80                  strlen(name),
81                  visstring,
82                  sizeof(visstring)),
83         cleartextpw == NULL ? "<empty>" : "<cleartextpw>");
84 
85    if (cleartextpw == NULL) {
86       /*
87        * No password to check.  I.e. the authmethod used does not care
88        * about passwords, only whether the user exists or not. E.g.
89        * rfc931/ident.
90        */
91       if (getpwnam(name) == NULL) {
92          snprintf(emsg, emsglen, "no user \"%s\" found in system password file",
93                   str2vis(name,
94                           strlen(name),
95                           visstring,
96                           sizeof(visstring)));
97          return -1;
98       }
99       else
100          /*
101           * User is in the passwordfile, and that is all we care about.
102           */
103          return 0;
104    }
105 
106    /*
107     * Else: the authmethod used requires us to match the password also.
108     */
109 
110    /* usually need privileges to look up the password. */
111    sockd_priv(SOCKD_PRIV_FILE_READ, PRIV_ON);
112    p = sockd_getpasswordhash(name,
113                              pwhash,
114                              sizeof(pwhash),
115                              emsg,
116                              emsglen);
117    sockd_priv(SOCKD_PRIV_FILE_READ, PRIV_OFF);
118 
119    if (p == NULL)
120       return -1;
121 
122    /*
123     * Have the passwordhash for the user.  Does it match the provided password?
124     */
125 
126    crypted = crypt(cleartextpw, pwhash);
127 
128    if (crypted == NULL) { /* strange. */
129       snprintf(emsg, emsglen,
130                "system password crypt(3) failed for user \"%s\": %s",
131                str2vis(name,
132                        strlen(name),
133                        visstring,
134                        sizeof(visstring)),
135                strerror(errno));
136 
137       swarnx("%s: Strange.  This should not happen: %s", function, emsg);
138       rc = -1;
139    }
140    else {
141       if (strcmp(crypted, pwhash) == 0)
142          rc = 0;
143       else {
144          snprintf(emsg, emsglen,
145                   "system password authentication failed for user \"%s\"",
146                   str2vis(name,
147                           strlen(name),
148                           visstring,
149                           sizeof(visstring)));
150          rc = -1;
151       }
152    }
153 
154    bzero(pwhash, sizeof(pwhash));
155    return rc;
156 }
157 
158 static const char *
sockd_getpasswordhash(login,pw,pwsize,emsg,emsglen)159 sockd_getpasswordhash(login, pw, pwsize, emsg, emsglen)
160    const char *login;
161    char *pw;
162    const size_t pwsize;
163    char *emsg;
164    const size_t emsglen;
165 {
166    const char *function = "socks_getencrypedpassword()";
167    const char *pw_db = NULL;
168    const int errno_s = errno;
169    char visstring[MAXNAMELEN * 4];
170 
171 #if HAVE_GETSPNAM /* sysv stuff. */
172    struct spwd *spwd;
173 
174    if ((spwd = getspnam(login)) != NULL)
175       pw_db = spwd->sp_pwdp;
176 
177 #elif HAVE_GETPRPWNAM /* some other broken stuff. */
178    /*
179     * don't know how this looks and don't know anybody using it.
180     */
181 
182 #error "getprpwnam() not supported yet.  Please contact Inferno Nettverk A/S "
183        "if you would like to see support for it."
184 
185 #elif HAVE_GETPWNAM_SHADOW /* OpenBSD 5.9 and later */
186 
187    struct passwd *pwd;
188 
189    if ((pwd = getpwnam_shadow(login)) != NULL)
190       pw_db = pwd->pw_passwd;
191 
192 #else /* normal BSD stuff. */
193    struct passwd *pwd;
194 
195    if ((pwd = getpwnam(login)) != NULL)
196       pw_db = pwd->pw_passwd;
197 #endif /* normal BSD stuff. */
198 
199    if (pw_db == NULL) {
200       snprintf(emsg, emsglen,
201                "could not access user \"%s\"'s records in the system "
202                "password file: %s",
203                str2vis(login, strlen(login), visstring, sizeof(visstring)),
204                strerror(errno));
205 
206       return NULL;
207    }
208 
209    if (strlen(pw_db) + 1 /* NUL */ > pwsize) {
210       snprintf(emsg, emsglen,
211                "%s: password set for user \"%s\" in the system password file "
212                "is too long.  The maximal supported length is %lu, but the "
213                "length of the password is %lu characters",
214                function,
215                str2vis(login,
216                       strlen(login),
217                       visstring,
218                       sizeof(visstring)),
219                (unsigned long)(pwsize - 1),
220                (unsigned long)strlen(pw_db));
221 
222       swarnx("%s: %s", function, emsg);
223       return NULL;
224    }
225 
226    strcpy(pw, pw_db);
227 
228    /*
229     * some systems can set errno even on success. :-/
230     * E.g. OpenBSD 4.4. seems to do this.  Looks like it tries
231     * /etc/spwd.db first, and if that fails, /etc/pwd.db, but it
232     * forgets to reset errno.
233     */
234    errno = errno_s;
235 
236    return pw;
237 }
238