pam_unix.c revision 1.1 1 1.1 christos /*-
2 1.1 christos * Copyright 1998 Juniper Networks, Inc.
3 1.1 christos * All rights reserved.
4 1.1 christos * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
5 1.1 christos * All rights reserved.
6 1.1 christos *
7 1.1 christos * Portions of this software was developed for the FreeBSD Project by
8 1.1 christos * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 1.1 christos * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
10 1.1 christos * ("CBOSS"), as part of the DARPA CHATS research program.
11 1.1 christos *
12 1.1 christos * Redistribution and use in source and binary forms, with or without
13 1.1 christos * modification, are permitted provided that the following conditions
14 1.1 christos * are met:
15 1.1 christos * 1. Redistributions of source code must retain the above copyright
16 1.1 christos * notice, this list of conditions and the following disclaimer.
17 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
18 1.1 christos * notice, this list of conditions and the following disclaimer in the
19 1.1 christos * documentation and/or other materials provided with the distribution.
20 1.1 christos * 3. The name of the author may not be used to endorse or promote
21 1.1 christos * products derived from this software without specific prior written
22 1.1 christos * permission.
23 1.1 christos *
24 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 1.1 christos * SUCH DAMAGE.
35 1.1 christos */
36 1.1 christos
37 1.1 christos #include <sys/cdefs.h>
38 1.1 christos __FBSDID("$FreeBSD: src/lib/libpam/modules/pam_unix/pam_unix.c,v 1.49 2004/02/10 10:13:21 des Exp $");
39 1.1 christos
40 1.1 christos #include <sys/param.h>
41 1.1 christos #include <sys/socket.h>
42 1.1 christos #include <sys/time.h>
43 1.1 christos #include <netinet/in.h>
44 1.1 christos #include <arpa/inet.h>
45 1.1 christos
46 1.1 christos #include <login_cap.h>
47 1.1 christos #include <netdb.h>
48 1.1 christos #include <pwd.h>
49 1.1 christos #include <stdlib.h>
50 1.1 christos #include <string.h>
51 1.1 christos #include <stdio.h>
52 1.1 christos #include <syslog.h>
53 1.1 christos #include <unistd.h>
54 1.1 christos
55 1.1 christos #include <libutil.h>
56 1.1 christos
57 1.1 christos #ifdef YP
58 1.1 christos #include <ypclnt.h>
59 1.1 christos #endif
60 1.1 christos
61 1.1 christos #define PAM_SM_AUTH
62 1.1 christos #define PAM_SM_ACCOUNT
63 1.1 christos #define PAM_SM_PASSWORD
64 1.1 christos
65 1.1 christos #include <security/pam_appl.h>
66 1.1 christos #include <security/pam_modules.h>
67 1.1 christos #include <security/pam_mod_misc.h>
68 1.1 christos
69 1.1 christos #define PASSWORD_HASH "md5"
70 1.1 christos #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
71 1.1 christos #define SALTSIZE 32
72 1.1 christos
73 1.1 christos static void makesalt(char []);
74 1.1 christos
75 1.1 christos static char password_hash[] = PASSWORD_HASH;
76 1.1 christos
77 1.1 christos #define PAM_OPT_LOCAL_PASS "local_pass"
78 1.1 christos #define PAM_OPT_NIS_PASS "nis_pass"
79 1.1 christos
80 1.1 christos char *tempname = NULL;
81 1.1 christos
82 1.1 christos /*
83 1.1 christos * authentication management
84 1.1 christos */
85 1.1 christos PAM_EXTERN int
86 1.1 christos pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
87 1.1 christos int argc __unused, const char *argv[] __unused)
88 1.1 christos {
89 1.1 christos login_cap_t *lc;
90 1.1 christos struct passwd *pwd;
91 1.1 christos int retval;
92 1.1 christos const char *pass, *user, *realpw, *prompt;
93 1.1 christos
94 1.1 christos if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) {
95 1.1 christos pwd = getpwnam(getlogin());
96 1.1 christos } else {
97 1.1 christos retval = pam_get_user(pamh, &user, NULL);
98 1.1 christos if (retval != PAM_SUCCESS)
99 1.1 christos return (retval);
100 1.1 christos pwd = getpwnam(user);
101 1.1 christos }
102 1.1 christos
103 1.1 christos PAM_LOG("Got user: %s", user);
104 1.1 christos
105 1.1 christos if (pwd != NULL) {
106 1.1 christos PAM_LOG("Doing real authentication");
107 1.1 christos realpw = pwd->pw_passwd;
108 1.1 christos if (realpw[0] == '\0') {
109 1.1 christos if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) &&
110 1.1 christos openpam_get_option(pamh, PAM_OPT_NULLOK))
111 1.1 christos return (PAM_SUCCESS);
112 1.1 christos realpw = "*";
113 1.1 christos }
114 1.1 christos lc = login_getpwclass(pwd);
115 1.1 christos } else {
116 1.1 christos PAM_LOG("Doing dummy authentication");
117 1.1 christos realpw = "*";
118 1.1 christos lc = login_getclass(NULL);
119 1.1 christos }
120 1.1 christos prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL);
121 1.1 christos retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt);
122 1.1 christos login_close(lc);
123 1.1 christos if (retval != PAM_SUCCESS)
124 1.1 christos return (retval);
125 1.1 christos PAM_LOG("Got password");
126 1.1 christos if (strcmp(crypt(pass, realpw), realpw) == 0)
127 1.1 christos return (PAM_SUCCESS);
128 1.1 christos
129 1.1 christos PAM_VERBOSE_ERROR("UNIX authentication refused");
130 1.1 christos return (PAM_AUTH_ERR);
131 1.1 christos }
132 1.1 christos
133 1.1 christos PAM_EXTERN int
134 1.1 christos pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
135 1.1 christos int argc __unused, const char *argv[] __unused)
136 1.1 christos {
137 1.1 christos
138 1.1 christos return (PAM_SUCCESS);
139 1.1 christos }
140 1.1 christos
141 1.1 christos /*
142 1.1 christos * account management
143 1.1 christos */
144 1.1 christos PAM_EXTERN int
145 1.1 christos pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused,
146 1.1 christos int argc __unused, const char *argv[] __unused)
147 1.1 christos {
148 1.1 christos struct addrinfo hints, *res;
149 1.1 christos struct passwd *pwd;
150 1.1 christos struct timeval tp;
151 1.1 christos login_cap_t *lc;
152 1.1 christos time_t warntime;
153 1.1 christos int retval;
154 1.1 christos const char *user;
155 1.1 christos const void *rhost, *tty;
156 1.1 christos char rhostip[MAXHOSTNAMELEN] = "";
157 1.1 christos
158 1.1 christos retval = pam_get_user(pamh, &user, NULL);
159 1.1 christos if (retval != PAM_SUCCESS)
160 1.1 christos return (retval);
161 1.1 christos
162 1.1 christos if (user == NULL || (pwd = getpwnam(user)) == NULL)
163 1.1 christos return (PAM_SERVICE_ERR);
164 1.1 christos
165 1.1 christos PAM_LOG("Got user: %s", user);
166 1.1 christos
167 1.1 christos retval = pam_get_item(pamh, PAM_RHOST, &rhost);
168 1.1 christos if (retval != PAM_SUCCESS)
169 1.1 christos return (retval);
170 1.1 christos
171 1.1 christos retval = pam_get_item(pamh, PAM_TTY, &tty);
172 1.1 christos if (retval != PAM_SUCCESS)
173 1.1 christos return (retval);
174 1.1 christos
175 1.1 christos if (*pwd->pw_passwd == '\0' &&
176 1.1 christos (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
177 1.1 christos return (PAM_NEW_AUTHTOK_REQD);
178 1.1 christos
179 1.1 christos lc = login_getpwclass(pwd);
180 1.1 christos if (lc == NULL) {
181 1.1 christos PAM_LOG("Unable to get login class for user %s", user);
182 1.1 christos return (PAM_SERVICE_ERR);
183 1.1 christos }
184 1.1 christos
185 1.1 christos PAM_LOG("Got login_cap");
186 1.1 christos
187 1.1 christos if (pwd->pw_change || pwd->pw_expire)
188 1.1 christos gettimeofday(&tp, NULL);
189 1.1 christos
190 1.1 christos /*
191 1.1 christos * Check pw_expire before pw_change - no point in letting the
192 1.1 christos * user change the password on an expired account.
193 1.1 christos */
194 1.1 christos
195 1.1 christos if (pwd->pw_expire) {
196 1.1 christos warntime = login_getcaptime(lc, "warnexpire",
197 1.1 christos DEFAULT_WARN, DEFAULT_WARN);
198 1.1 christos if (tp.tv_sec >= pwd->pw_expire) {
199 1.1 christos login_close(lc);
200 1.1 christos return (PAM_ACCT_EXPIRED);
201 1.1 christos } else if (pwd->pw_expire - tp.tv_sec < warntime &&
202 1.1 christos (flags & PAM_SILENT) == 0) {
203 1.1 christos pam_error(pamh, "Warning: your account expires on %s",
204 1.1 christos ctime(&pwd->pw_expire));
205 1.1 christos }
206 1.1 christos }
207 1.1 christos
208 1.1 christos retval = PAM_SUCCESS;
209 1.1 christos if (pwd->pw_change) {
210 1.1 christos warntime = login_getcaptime(lc, "warnpassword",
211 1.1 christos DEFAULT_WARN, DEFAULT_WARN);
212 1.1 christos if (tp.tv_sec >= pwd->pw_change) {
213 1.1 christos retval = PAM_NEW_AUTHTOK_REQD;
214 1.1 christos } else if (pwd->pw_change - tp.tv_sec < warntime &&
215 1.1 christos (flags & PAM_SILENT) == 0) {
216 1.1 christos pam_error(pamh, "Warning: your password expires on %s",
217 1.1 christos ctime(&pwd->pw_change));
218 1.1 christos }
219 1.1 christos }
220 1.1 christos
221 1.1 christos /*
222 1.1 christos * From here on, we must leave retval untouched (unless we
223 1.1 christos * know we're going to fail), because we need to remember
224 1.1 christos * whether we're supposed to return PAM_SUCCESS or
225 1.1 christos * PAM_NEW_AUTHTOK_REQD.
226 1.1 christos */
227 1.1 christos
228 1.1 christos if (rhost && *(const char *)rhost != '\0') {
229 1.1 christos memset(&hints, 0, sizeof(hints));
230 1.1 christos hints.ai_family = AF_UNSPEC;
231 1.1 christos if (getaddrinfo(rhost, NULL, &hints, &res) == 0) {
232 1.1 christos getnameinfo(res->ai_addr, res->ai_addrlen,
233 1.1 christos rhostip, sizeof(rhostip), NULL, 0,
234 1.1 christos NI_NUMERICHOST|NI_WITHSCOPEID);
235 1.1 christos }
236 1.1 christos if (res != NULL)
237 1.1 christos freeaddrinfo(res);
238 1.1 christos }
239 1.1 christos
240 1.1 christos /*
241 1.1 christos * Check host / tty / time-of-day restrictions
242 1.1 christos */
243 1.1 christos
244 1.1 christos if (!auth_hostok(lc, rhost, rhostip) ||
245 1.1 christos !auth_ttyok(lc, tty) ||
246 1.1 christos !auth_timeok(lc, time(NULL)))
247 1.1 christos retval = PAM_AUTH_ERR;
248 1.1 christos
249 1.1 christos login_close(lc);
250 1.1 christos
251 1.1 christos return (retval);
252 1.1 christos }
253 1.1 christos
254 1.1 christos /*
255 1.1 christos * password management
256 1.1 christos *
257 1.1 christos * standard Unix and NIS password changing
258 1.1 christos */
259 1.1 christos PAM_EXTERN int
260 1.1 christos pam_sm_chauthtok(pam_handle_t *pamh, int flags,
261 1.1 christos int argc __unused, const char *argv[] __unused)
262 1.1 christos {
263 1.1 christos #ifdef YP
264 1.1 christos struct ypclnt *ypclnt;
265 1.1 christos void *yp_domain, *yp_server;
266 1.1 christos #endif
267 1.1 christos char salt[SALTSIZE + 1];
268 1.1 christos login_cap_t * lc;
269 1.1 christos struct passwd *pwd, *old_pwd;
270 1.1 christos const char *user, *old_pass, *new_pass;
271 1.1 christos char *encrypted;
272 1.1 christos int pfd, tfd, retval;
273 1.1 christos
274 1.1 christos if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF))
275 1.1 christos pwd = getpwnam(getlogin());
276 1.1 christos else {
277 1.1 christos retval = pam_get_user(pamh, &user, NULL);
278 1.1 christos if (retval != PAM_SUCCESS)
279 1.1 christos return (retval);
280 1.1 christos pwd = getpwnam(user);
281 1.1 christos }
282 1.1 christos
283 1.1 christos if (pwd == NULL)
284 1.1 christos return (PAM_AUTHTOK_RECOVERY_ERR);
285 1.1 christos
286 1.1 christos PAM_LOG("Got user: %s", user);
287 1.1 christos
288 1.1 christos if (flags & PAM_PRELIM_CHECK) {
289 1.1 christos
290 1.1 christos PAM_LOG("PRELIM round");
291 1.1 christos
292 1.1 christos if (getuid() == 0 &&
293 1.1 christos (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES)
294 1.1 christos /* root doesn't need the old password */
295 1.1 christos return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
296 1.1 christos #ifdef YP
297 1.1 christos if (getuid() == 0 &&
298 1.1 christos (pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
299 1.1 christos
300 1.1 christos yp_domain = yp_server = NULL;
301 1.1 christos (void)pam_get_data(pamh, "yp_domain", &yp_domain);
302 1.1 christos (void)pam_get_data(pamh, "yp_server", &yp_server);
303 1.1 christos
304 1.1 christos ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server);
305 1.1 christos if (ypclnt == NULL)
306 1.1 christos return (PAM_BUF_ERR);
307 1.1 christos
308 1.1 christos if (ypclnt_connect(ypclnt) == -1) {
309 1.1 christos ypclnt_free(ypclnt);
310 1.1 christos return (PAM_SERVICE_ERR);
311 1.1 christos }
312 1.1 christos
313 1.1 christos retval = ypclnt_havepasswdd(ypclnt);
314 1.1 christos ypclnt_free(ypclnt);
315 1.1 christos if (retval == 1)
316 1.1 christos return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
317 1.1 christos else if (retval == -1)
318 1.1 christos return (PAM_SERVICE_ERR);
319 1.1 christos }
320 1.1 christos #endif
321 1.1 christos if (pwd->pw_passwd[0] == '\0'
322 1.1 christos && openpam_get_option(pamh, PAM_OPT_NULLOK)) {
323 1.1 christos /*
324 1.1 christos * No password case. XXX Are we giving too much away
325 1.1 christos * by not prompting for a password?
326 1.1 christos * XXX check PAM_DISALLOW_NULL_AUTHTOK
327 1.1 christos */
328 1.1 christos old_pass = "";
329 1.1 christos } else {
330 1.1 christos retval = pam_get_authtok(pamh,
331 1.1 christos PAM_OLDAUTHTOK, &old_pass, NULL);
332 1.1 christos if (retval != PAM_SUCCESS)
333 1.1 christos return (retval);
334 1.1 christos }
335 1.1 christos PAM_LOG("Got old password");
336 1.1 christos /* always encrypt first */
337 1.1 christos encrypted = crypt(old_pass, pwd->pw_passwd);
338 1.1 christos if (old_pass[0] == '\0' &&
339 1.1 christos !openpam_get_option(pamh, PAM_OPT_NULLOK))
340 1.1 christos return (PAM_PERM_DENIED);
341 1.1 christos if (strcmp(encrypted, pwd->pw_passwd) != 0)
342 1.1 christos return (PAM_PERM_DENIED);
343 1.1 christos }
344 1.1 christos else if (flags & PAM_UPDATE_AUTHTOK) {
345 1.1 christos PAM_LOG("UPDATE round");
346 1.1 christos
347 1.1 christos retval = pam_get_authtok(pamh,
348 1.1 christos PAM_OLDAUTHTOK, &old_pass, NULL);
349 1.1 christos if (retval != PAM_SUCCESS)
350 1.1 christos return (retval);
351 1.1 christos PAM_LOG("Got old password");
352 1.1 christos
353 1.1 christos /* get new password */
354 1.1 christos for (;;) {
355 1.1 christos retval = pam_get_authtok(pamh,
356 1.1 christos PAM_AUTHTOK, &new_pass, NULL);
357 1.1 christos if (retval != PAM_TRY_AGAIN)
358 1.1 christos break;
359 1.1 christos pam_error(pamh, "Mismatch; try again, EOF to quit.");
360 1.1 christos }
361 1.1 christos PAM_LOG("Got new password");
362 1.1 christos if (retval != PAM_SUCCESS) {
363 1.1 christos PAM_VERBOSE_ERROR("Unable to get new password");
364 1.1 christos return (retval);
365 1.1 christos }
366 1.1 christos
367 1.1 christos if (getuid() != 0 && new_pass[0] == '\0' &&
368 1.1 christos !openpam_get_option(pamh, PAM_OPT_NULLOK))
369 1.1 christos return (PAM_PERM_DENIED);
370 1.1 christos
371 1.1 christos if ((old_pwd = pw_dup(pwd)) == NULL)
372 1.1 christos return (PAM_BUF_ERR);
373 1.1 christos
374 1.1 christos pwd->pw_change = 0;
375 1.1 christos lc = login_getclass(NULL);
376 1.1 christos if (login_setcryptfmt(lc, password_hash, NULL) == NULL)
377 1.1 christos openpam_log(PAM_LOG_ERROR,
378 1.1 christos "can't set password cipher, relying on default");
379 1.1 christos login_close(lc);
380 1.1 christos makesalt(salt);
381 1.1 christos pwd->pw_passwd = crypt(new_pass, salt);
382 1.1 christos #ifdef YP
383 1.1 christos switch (old_pwd->pw_fields & _PWF_SOURCE) {
384 1.1 christos case _PWF_FILES:
385 1.1 christos #endif
386 1.1 christos retval = PAM_SERVICE_ERR;
387 1.1 christos if (pw_init(NULL, NULL))
388 1.1 christos openpam_log(PAM_LOG_ERROR, "pw_init() failed");
389 1.1 christos else if ((pfd = pw_lock()) == -1)
390 1.1 christos openpam_log(PAM_LOG_ERROR, "pw_lock() failed");
391 1.1 christos else if ((tfd = pw_tmp(-1)) == -1)
392 1.1 christos openpam_log(PAM_LOG_ERROR, "pw_tmp() failed");
393 1.1 christos else if (pw_copy(pfd, tfd, pwd, old_pwd) == -1)
394 1.1 christos openpam_log(PAM_LOG_ERROR, "pw_copy() failed");
395 1.1 christos else if (pw_mkdb(pwd->pw_name) == -1)
396 1.1 christos openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed");
397 1.1 christos else
398 1.1 christos retval = PAM_SUCCESS;
399 1.1 christos pw_fini();
400 1.1 christos #ifdef YP
401 1.1 christos break;
402 1.1 christos case _PWF_NIS:
403 1.1 christos yp_domain = yp_server = NULL;
404 1.1 christos (void)pam_get_data(pamh, "yp_domain", &yp_domain);
405 1.1 christos (void)pam_get_data(pamh, "yp_server", &yp_server);
406 1.1 christos ypclnt = ypclnt_new(yp_domain,
407 1.1 christos "passwd.byname", yp_server);
408 1.1 christos if (ypclnt == NULL) {
409 1.1 christos retval = PAM_BUF_ERR;
410 1.1 christos } else if (ypclnt_connect(ypclnt) == -1 ||
411 1.1 christos ypclnt_passwd(ypclnt, pwd, old_pass) == -1) {
412 1.1 christos openpam_log(PAM_LOG_ERROR, "%s", ypclnt->error);
413 1.1 christos retval = PAM_SERVICE_ERR;
414 1.1 christos } else {
415 1.1 christos retval = PAM_SUCCESS;
416 1.1 christos }
417 1.1 christos ypclnt_free(ypclnt);
418 1.1 christos break;
419 1.1 christos default:
420 1.1 christos openpam_log(PAM_LOG_ERROR, "unsupported source 0x%x",
421 1.1 christos pwd->pw_fields & _PWF_SOURCE);
422 1.1 christos retval = PAM_SERVICE_ERR;
423 1.1 christos }
424 1.1 christos #endif
425 1.1 christos free(old_pwd);
426 1.1 christos }
427 1.1 christos else {
428 1.1 christos /* Very bad juju */
429 1.1 christos retval = PAM_ABORT;
430 1.1 christos PAM_LOG("Illegal 'flags'");
431 1.1 christos }
432 1.1 christos
433 1.1 christos return (retval);
434 1.1 christos }
435 1.1 christos
436 1.1 christos /* Mostly stolen from passwd(1)'s local_passwd.c - markm */
437 1.1 christos
438 1.1 christos static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
439 1.1 christos "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
440 1.1 christos
441 1.1 christos static void
442 1.1 christos to64(char *s, long v, int n)
443 1.1 christos {
444 1.1 christos while (--n >= 0) {
445 1.1 christos *s++ = itoa64[v&0x3f];
446 1.1 christos v >>= 6;
447 1.1 christos }
448 1.1 christos }
449 1.1 christos
450 1.1 christos /* Salt suitable for traditional DES and MD5 */
451 1.1 christos void
452 1.1 christos makesalt(char salt[SALTSIZE])
453 1.1 christos {
454 1.1 christos int i;
455 1.1 christos
456 1.1 christos /* These are not really random numbers, they are just
457 1.1 christos * numbers that change to thwart construction of a
458 1.1 christos * dictionary. This is exposed to the public.
459 1.1 christos */
460 1.1 christos for (i = 0; i < SALTSIZE; i += 4)
461 1.1 christos to64(&salt[i], arc4random(), 4);
462 1.1 christos salt[SALTSIZE] = '\0';
463 1.1 christos }
464 1.1 christos
465 1.1 christos PAM_MODULE_ENTRY("pam_unix");
466