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#ifdef __GNUC__
31#ident "$Id: auth_shadow.c,v 1.12 2009/08/14 14:58:38 mel Exp $"
32#endif
33
34/* PUBLIC DEPENDENCIES */
35#include "mechanisms.h"
36
37#ifdef AUTH_SHADOW
38
39#define PWBUFSZ 256 /***SWB***/
40
41# include <unistd.h>
42# include <stdlib.h>
43# include <string.h>
44# include <sys/types.h>
45# include <time.h>
46# include <pwd.h>
47# include <errno.h>
48# include <syslog.h>
49
50#ifdef HAVE_CRYPT_H
51#include <crypt.h>
52#endif
53
54# ifndef HAVE_GETSPNAM
55
56# ifdef WITH_DES
57#  ifdef WITH_SSL_DES
58#   include <openssl/des.h>
59#  else
60#   include <des.h>
61#  endif /* WITH_SSL_DES */
62# endif /* WITH_DES */
63
64# endif /* ! HAVE_GETSPNAM */
65
66# ifdef HAVE_GETUSERPW
67#  include <userpw.h>
68#  include <usersec.h>
69# else /* ! HAVE_GETUSERPW */
70#  include <shadow.h>
71# endif /* ! HAVE_GETUSERPW */
72
73# include "auth_shadow.h"
74# include "globals.h"
75/* END PUBLIC DEPENDENCIES */
76
77/* FUNCTION: auth_shadow */
78
79/* SYNOPSIS
80 * Authenticate against the system shadow password database. Where
81 * possible (and if enabled by the command line arguments), enforce
82 * time-of-day and other login restrictions.
83 */
84
85char *					/* R: allocated response string */
86auth_shadow (
87  /* PARAMETERS */
88  const char *login,			/* I: plaintext authenticator */
89  const char *password,			/* I: plaintext password */
90  const char *service __attribute__((unused)),
91  const char *realm __attribute__((unused))
92  /* END PARAMETERS */
93  )
94{
95
96/************************************************************************
97 *									*
98 * This is gross. Everyone wants to do this differently, thus we have   *
99 * to #ifdef the whole mess for each system type.                       *
100 *									*
101 ***********************************************************************/
102
103# ifdef HAVE_GETSPNAM
104
105 /***************
106 * getspnam_r() *
107 ***************/
108
109    /* VARIABLES */
110    long today;				/* the current time */
111    char *cpw;				/* pointer to crypt() result */
112    struct passwd	*pw;		/* return from getpwnam_r() */
113    struct spwd   	*sp;		/* return from getspnam_r() */
114    int errnum;
115#  ifdef _REENTRANT
116    struct passwd pwbuf;
117    char pwdata[PWBUFSZ];		/* pwbuf indirect data goes in here */
118
119    struct spwd spbuf;
120    char spdata[PWBUFSZ];		/* spbuf indirect data goes in here */
121#  endif /* _REENTRANT */
122    /* END VARIABLES */
123
124#  define RETURN(x) return strdup(x)
125
126    /*
127     * "Magic" password field entries for SunOS/SysV
128     *
129     * "*LK*" is defined at in the shadow(4) man page, but of course any string
130     * inserted in front of the password will prevent the strings from matching
131     *
132     * *NP* is documented in getspnam(3) and indicates the caller had
133     * insufficient permission to read the shadow password database
134     * (generally this is a NIS error).
135     */
136
137#  define SHADOW_PW_LOCKED "*LK*"	/* account locked (not used by us) */
138#  define SHADOW_PW_EPERM  "*NP*"	/* insufficient database perms */
139
140#  ifdef _REENTRANT
141#    ifdef GETXXNAM_R_5ARG
142	(void) getpwnam_r(login, &pwbuf, pwdata, sizeof(pwdata), &pw);
143#    else
144    pw = getpwnam_r(login, &pwbuf, pwdata, sizeof(pwdata));
145#    endif /* GETXXNAM_R_5ARG */
146#  else
147    pw = getpwnam(login);
148#  endif /* _REENTRANT */
149    errnum = errno;
150    endpwent();
151
152    if (pw == NULL) {
153	if (errnum != 0) {
154	    char *errstr;
155
156	    if (flags & VERBOSE) {
157		syslog(LOG_DEBUG, "DEBUG: auth_shadow: getpwnam(%s) failure: %m", login);
158	    }
159	    if (asprintf(&errstr, "NO Username lookup failure: %s", strerror(errno)) == -1) {
160		/* XXX the hidden strdup() will likely fail and return NULL here.... */
161		RETURN("NO Username lookup failure: unknown error (ENOMEM formatting strerror())");
162	    }
163	    return errstr;
164	} else {
165	    if (flags & VERBOSE) {
166		syslog(LOG_DEBUG, "DEBUG: auth_shadow: getpwnam(%s): invalid username", login);
167	    }
168	    RETURN("NO Invalid username");
169	}
170    }
171
172    today = (long)time(NULL)/(24L*60*60);
173
174#  ifdef _REENTRANT
175#    ifdef GETXXNAM_R_5ARG
176	(void) getspnam_r(login, &spbuf, spdata, sizeof(spdata), &sp);
177#    else
178    sp = getspnam_r(login, &spbuf, spdata, sizeof(spdata));
179#    endif /* GETXXNAM_R_5ARG */
180#  else
181    sp = getspnam(login);
182#  endif /* _REENTRANT */
183    errnum = errno;
184    endspent();
185
186    if (sp == NULL) {
187	if (errnum != 0) {
188	    char *errstr;
189
190	    if (flags & VERBOSE) {
191		syslog(LOG_DEBUG, "DEBUG: auth_shadow: getspnam(%s) failure: %m", login);
192	    }
193	    if (asprintf(&errstr, "NO Username shadow lookup failure: %s", strerror(errno)) == -1) {
194		/* XXX the hidden strdup() will likely fail and return NULL here.... */
195		RETURN("NO Username shadow lookup failure: unknown error (ENOMEM formatting strerror())");
196	    }
197	    return errstr;
198	} else {
199	    if (flags & VERBOSE) {
200		syslog(LOG_DEBUG, "DEBUG: auth_shadow: getspnam(%s): invalid shadow username", login);
201	    }
202	    RETURN("NO Invalid shadow username");
203	}
204    }
205
206    if (!strcmp(sp->sp_pwdp, SHADOW_PW_EPERM)) {
207	if (flags & VERBOSE) {
208	    syslog(LOG_DEBUG, "DEBUG: auth_shadow: sp->sp_pwdp == SHADOW_PW_EPERM");
209	}
210	RETURN("NO Insufficient permission to access NIS authentication database (saslauthd)");
211    }
212
213    cpw = strdup((const char *)crypt(password, sp->sp_pwdp));
214    if (strcmp(sp->sp_pwdp, cpw)) {
215	if (flags & VERBOSE) {
216	    /*
217	     * This _should_ reveal the SHADOW_PW_LOCKED prefix to an
218	     * administrator trying to debug the situation, though maybe we
219	     * should do the check here and be less obtuse about it....
220	     */
221	    syslog(LOG_DEBUG, "DEBUG: auth_shadow: pw mismatch: '%s' != '%s'",
222		   sp->sp_pwdp, cpw);
223	}
224	free(cpw);
225	RETURN("NO Incorrect password");
226    }
227    free(cpw);
228
229    /*
230     * The following fields will be set to -1 if:
231     *
232     *	1) They are not specified in the shadow database, or
233     *	2) The database is being served up by NIS.
234     */
235
236    if ((sp->sp_expire != -1) && (today > sp->sp_expire)) {
237	if (flags & VERBOSE) {
238	    syslog(LOG_DEBUG, "DEBUG: auth_shadow: account expired: %dl > %dl",
239		   today, sp->sp_expire);
240	}
241	RETURN("NO Account expired");
242    }
243
244    /* Remaining tests are relative to the last change date for the password */
245
246    if (sp->sp_lstchg != -1) {
247
248	if ((sp->sp_max != -1) && ((sp->sp_lstchg + sp->sp_max) < today)) {
249	    if (flags & VERBOSE) {
250		syslog(LOG_DEBUG,
251		       "DEBUG: auth_shadow: password expired: %ld + %ld < %ld",
252		       sp->sp_lstchg, sp->sp_max, today);
253	    }
254	    RETURN("NO Password expired");
255	}
256    }
257    if (flags & VERBOSE) {
258	syslog(LOG_DEBUG, "DEBUG: auth_shadow: OK: %s", login);
259    }
260    RETURN("OK");
261
262
263# elif defined(HAVE_GETUSERPW)
264
265/*************
266 * AIX 4.1.4 *
267 ************/
268    /* VARIABLES */
269    struct userpw *upw;				/* return from getuserpw() */
270    /* END VARIABLES */
271
272#  define RETURN(x) { endpwdb(); return strdup(x); }
273
274    if (setpwdb(S_READ) == -1) {
275	syslog(LOG_ERR, "setpwdb: %m");
276	RETURN("NO setpwdb() internal failure (saslauthd)");
277    }
278
279    upw = getuserpw(login);
280
281    if (upw == 0) {
282	if (flags & VERBOSE) {
283	    syslog(LOG_DEBUG, "auth_shadow: getuserpw(%s) failed: %m",
284		   login);
285	}
286	RETURN("NO Invalid username");
287    }
288
289    if (strcmp(upw->upw_passwd, crypt(password, upw->upw_passwd)) != 0) {
290	if (flags & VERBOSE) {
291	    syslog(LOG_DEBUG, "auth_shadow: pw mismatch: %s != %s",
292		   password, upw->upw_passwd);
293	}
294	RETURN("NO Incorrect password");
295    }
296
297    RETURN("OK");
298
299# else /* HAVE_GETUSERPW */
300
301#  error "unknown shadow authentication type"
302
303# endif /* ! HAVE_GETUSERPW */
304}
305
306#else /* !AUTH_SHADOW */
307
308char *
309auth_shadow (
310  const char *login __attribute__((unused)),
311  const char *passwd __attribute__((unused)),
312  const char *service __attribute__((unused)),
313  const char *realm __attribute__((unused))
314  )
315{
316    return NULL;
317}
318
319#endif /* !AUTH_SHADOW */
320
321/* END FUNCTION: auth_shadow */
322
323/* END MODULE: auth_shadow */
324