1 /* MODULE: auth_shadow */
2
3 /* COPYRIGHT
4 * Copyright (c) 1997 Messaging Direct Ltd.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY MESSAGING DIRECT LTD. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MESSAGING DIRECT LTD. OR
20 * ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27 * DAMAGE.
28 * END COPYRIGHT */
29
30 #include <config.h>
31
32 /* PUBLIC DEPENDENCIES */
33 #include "mechanisms.h"
34 #include <stdio.h>
35
36 #ifdef AUTH_SHADOW
37
38 #ifndef _XOPEN_SOURCE
39 #define _XOPEN_SOURCE
40 #endif
41 #define PWBUFSZ 256 /***SWB***/
42
43 # include <unistd.h>
44 # include <stdlib.h>
45 # include <string.h>
46 # include <sys/types.h>
47 # include <time.h>
48 # include <pwd.h>
49 # include <errno.h>
50 # include <syslog.h>
51
52 #ifdef HAVE_CRYPT_H
53 #include <crypt.h>
54 #endif
55
56 # ifndef HAVE_GETSPNAM
57
58 # ifdef WITH_DES
59 # ifdef WITH_SSL_DES
60 # include <openssl/des.h>
61 # else
62 # include <des.h>
63 # endif /* WITH_SSL_DES */
64 # endif /* WITH_DES */
65
66 # endif /* ! HAVE_GETSPNAM */
67
68 # ifdef HAVE_GETUSERPW
69 # include <userpw.h>
70 # include <usersec.h>
71 # else /* ! HAVE_GETUSERPW */
72 # include <shadow.h>
73 # endif /* ! HAVE_GETUSERPW */
74
75 # ifdef HAVE_CRYPT_H
76 # include <crypt.h>
77 # endif
78
79 # include "auth_shadow.h"
80 # include "globals.h"
81 /* END PUBLIC DEPENDENCIES */
82
83 /* FUNCTION: auth_shadow */
84
85 /* SYNOPSIS
86 * Authenticate against the system shadow password database. Where
87 * possible (and if enabled by the command line arguments), enforce
88 * time-of-day and other login restrictions.
89 */
90
91 char * /* R: allocated response string */
auth_shadow(const char * login,const char * password,const char * service,const char * realm)92 auth_shadow (
93 /* PARAMETERS */
94 const char *login, /* I: plaintext authenticator */
95 const char *password, /* I: plaintext password */
96 const char *service __attribute__((unused)),
97 const char *realm __attribute__((unused))
98 /* END PARAMETERS */
99 )
100 {
101
102 /************************************************************************
103 * *
104 * This is gross. Everyone wants to do this differently, thus we have *
105 * to #ifdef the whole mess for each system type. *
106 * *
107 ***********************************************************************/
108
109 # ifdef HAVE_GETSPNAM
110
111 /***************
112 * getspnam_r() *
113 ***************/
114
115 /* VARIABLES */
116 long today; /* the current time */
117 char *cpw; /* pointer to crypt() result */
118 struct passwd *pw; /* return from getpwnam_r() */
119 struct spwd *sp; /* return from getspnam_r() */
120 int errnum;
121 # ifdef _REENTRANT
122 struct passwd pwbuf;
123 char pwdata[PWBUFSZ]; /* pwbuf indirect data goes in here */
124
125 struct spwd spbuf;
126 char spdata[PWBUFSZ]; /* spbuf indirect data goes in here */
127
128 struct crypt_data cdata;
129 # endif /* _REENTRANT */
130 /* END VARIABLES */
131
132 # define RETURN(x) return strdup(x)
133
134 /*
135 * "Magic" password field entries for SunOS/SysV
136 *
137 * "*LK*" is defined at in the shadow(4) man page, but of course any string
138 * inserted in front of the password will prevent the strings from matching
139 *
140 * *NP* is documented in getspnam(3) and indicates the caller had
141 * insufficient permission to read the shadow password database
142 * (generally this is a NIS error).
143 */
144
145 # define SHADOW_PW_LOCKED "*LK*" /* account locked (not used by us) */
146 # define SHADOW_PW_EPERM "*NP*" /* insufficient database perms */
147
148 # ifdef _REENTRANT
149 # ifdef GETXXNAM_R_5ARG
150 (void) getpwnam_r(login, &pwbuf, pwdata, sizeof(pwdata), &pw);
151 # else
152 pw = getpwnam_r(login, &pwbuf, pwdata, sizeof(pwdata));
153 # endif /* GETXXNAM_R_5ARG */
154 # else
155 pw = getpwnam(login);
156 # endif /* _REENTRANT */
157 errnum = errno;
158 endpwent();
159
160 if (pw == NULL) {
161 if (errnum != 0) {
162 char *errstr;
163
164 if (flags & VERBOSE) {
165 syslog(LOG_DEBUG, "DEBUG: auth_shadow: getpwnam(%s) failure: %m", login);
166 }
167 if (asprintf(&errstr, "NO Username lookup failure: %s", strerror(errno)) == -1) {
168 /* XXX the hidden strdup() will likely fail and return NULL here.... */
169 RETURN("NO Username lookup failure: unknown error (ENOMEM formatting strerror())");
170 }
171 return errstr;
172 } else {
173 if (flags & VERBOSE) {
174 syslog(LOG_DEBUG, "DEBUG: auth_shadow: getpwnam(%s): invalid username", login);
175 }
176 RETURN("NO Invalid username");
177 }
178 }
179
180 today = (long)time(NULL)/(24L*60*60);
181
182 # ifdef _REENTRANT
183 # ifdef GETXXNAM_R_5ARG
184 (void) getspnam_r(login, &spbuf, spdata, sizeof(spdata), &sp);
185 # else
186 sp = getspnam_r(login, &spbuf, spdata, sizeof(spdata));
187 # endif /* GETXXNAM_R_5ARG */
188 # else
189 sp = getspnam(login);
190 # endif /* _REENTRANT */
191 errnum = errno;
192 endspent();
193
194 if (sp == NULL) {
195 if (errnum != 0) {
196 char *errstr;
197
198 if (flags & VERBOSE) {
199 syslog(LOG_DEBUG, "DEBUG: auth_shadow: getspnam(%s) failure: %m", login);
200 }
201 if (asprintf(&errstr, "NO Username shadow lookup failure: %s", strerror(errno)) == -1) {
202 /* XXX the hidden strdup() will likely fail and return NULL here.... */
203 RETURN("NO Username shadow lookup failure: unknown error (ENOMEM formatting strerror())");
204 }
205 return errstr;
206 } else {
207 if (flags & VERBOSE) {
208 syslog(LOG_DEBUG, "DEBUG: auth_shadow: getspnam(%s): invalid shadow username", login);
209 }
210 RETURN("NO Invalid shadow username");
211 }
212 }
213
214 if (!strcmp(sp->sp_pwdp, SHADOW_PW_EPERM)) {
215 if (flags & VERBOSE) {
216 syslog(LOG_DEBUG, "DEBUG: auth_shadow: sp->sp_pwdp == SHADOW_PW_EPERM");
217 }
218 RETURN("NO Insufficient permission to access NIS authentication database (saslauthd)");
219 }
220
221 # ifdef _REENTRANT
222 cdata.initialized = 0;
223 cpw = crypt_r(password, sp->sp_pwdp, &cdata);
224 # else
225 cpw = crypt(password, sp->sp_pwdp);
226 # endif /* _REENTRANT */
227 if (!cpw || strcmp(sp->sp_pwdp, (const char *)cpw)) {
228 if (flags & VERBOSE) {
229 /*
230 * This _should_ reveal the SHADOW_PW_LOCKED prefix to an
231 * administrator trying to debug the situation, though maybe we
232 * should do the check here and be less obtuse about it....
233 */
234 syslog(LOG_DEBUG, "DEBUG: auth_shadow: pw mismatch: '%s' != '%s'",
235 sp->sp_pwdp, cpw);
236 }
237 RETURN("NO Incorrect password");
238 }
239
240 /*
241 * The following fields will be set to -1 if:
242 *
243 * 1) They are not specified in the shadow database, or
244 * 2) The database is being served up by NIS.
245 */
246
247 if ((sp->sp_expire != -1) && (today > sp->sp_expire)) {
248 if (flags & VERBOSE) {
249 syslog(LOG_DEBUG, "DEBUG: auth_shadow: account expired: %ld > %ld",
250 today, sp->sp_expire);
251 }
252 RETURN("NO Account expired");
253 }
254
255 /* Remaining tests are relative to the last change date for the password */
256
257 if (sp->sp_lstchg != -1) {
258
259 if ((sp->sp_max != -1) && ((sp->sp_lstchg + sp->sp_max) < today)) {
260 if (flags & VERBOSE) {
261 syslog(LOG_DEBUG,
262 "DEBUG: auth_shadow: password expired: %ld + %ld < %ld",
263 sp->sp_lstchg, sp->sp_max, today);
264 }
265 RETURN("NO Password expired");
266 }
267 }
268 if (flags & VERBOSE) {
269 syslog(LOG_DEBUG, "DEBUG: auth_shadow: OK: %s", login);
270 }
271 RETURN("OK");
272
273
274 # elif defined(HAVE_GETUSERPW)
275
276 /*************
277 * AIX 4.1.4 *
278 ************/
279 /* VARIABLES */
280 struct userpw *upw; /* return from getuserpw() */
281 /* END VARIABLES */
282
283 # define RETURN(x) { endpwdb(); return strdup(x); }
284
285 if (setpwdb(S_READ) == -1) {
286 syslog(LOG_ERR, "setpwdb: %m");
287 RETURN("NO setpwdb() internal failure (saslauthd)");
288 }
289
290 upw = getuserpw(login);
291
292 if (upw == 0) {
293 if (flags & VERBOSE) {
294 syslog(LOG_DEBUG, "auth_shadow: getuserpw(%s) failed: %m",
295 login);
296 }
297 RETURN("NO Invalid username");
298 }
299
300 if (!(cpw = crypt(password, upw->upw_passwd)) || (strcmp(upw->upw_passwd, (const char *)cpw) != 0)) {
301 if (flags & VERBOSE) {
302 syslog(LOG_DEBUG, "auth_shadow: pw mismatch: %s != %s",
303 password, upw->upw_passwd);
304 }
305 RETURN("NO Incorrect password");
306 }
307
308 RETURN("OK");
309
310 # else /* HAVE_GETUSERPW */
311
312 # error "unknown shadow authentication type"
313
314 # endif /* ! HAVE_GETUSERPW */
315 }
316
317 #else /* !AUTH_SHADOW */
318
319 char *
auth_shadow(const char * login,const char * passwd,const char * service,const char * realm)320 auth_shadow (
321 const char *login __attribute__((unused)),
322 const char *passwd __attribute__((unused)),
323 const char *service __attribute__((unused)),
324 const char *realm __attribute__((unused))
325 )
326 {
327 return NULL;
328 }
329
330 #endif /* !AUTH_SHADOW */
331
332 /* END FUNCTION: auth_shadow */
333
334 /* END MODULE: auth_shadow */
335