xref: /freebsd/contrib/pam-krb5/tests/fakepam/data.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert  * Data manipulation functions for the fake PAM library, used for testing.
3*bf6873c5SCy Schubert  *
4*bf6873c5SCy Schubert  * This file contains the implementation of pam_get_* and pam_set_* for the
5*bf6873c5SCy Schubert  * various data items supported by the PAM library, plus the PAM environment
6*bf6873c5SCy Schubert  * manipulation functions.
7*bf6873c5SCy Schubert  *
8*bf6873c5SCy Schubert  * The canonical version of this file is maintained in the rra-c-util package,
9*bf6873c5SCy Schubert  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
10*bf6873c5SCy Schubert  *
11*bf6873c5SCy Schubert  * Written by Russ Allbery <eagle@eyrie.org>
12*bf6873c5SCy Schubert  * Copyright 2017, 2020 Russ Allbery <eagle@eyrie.org>
13*bf6873c5SCy Schubert  * Copyright 2010-2011, 2014
14*bf6873c5SCy Schubert  *     The Board of Trustees of the Leland Stanford Junior University
15*bf6873c5SCy Schubert  *
16*bf6873c5SCy Schubert  * Permission is hereby granted, free of charge, to any person obtaining a
17*bf6873c5SCy Schubert  * copy of this software and associated documentation files (the "Software"),
18*bf6873c5SCy Schubert  * to deal in the Software without restriction, including without limitation
19*bf6873c5SCy Schubert  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20*bf6873c5SCy Schubert  * and/or sell copies of the Software, and to permit persons to whom the
21*bf6873c5SCy Schubert  * Software is furnished to do so, subject to the following conditions:
22*bf6873c5SCy Schubert  *
23*bf6873c5SCy Schubert  * The above copyright notice and this permission notice shall be included in
24*bf6873c5SCy Schubert  * all copies or substantial portions of the Software.
25*bf6873c5SCy Schubert  *
26*bf6873c5SCy Schubert  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27*bf6873c5SCy Schubert  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28*bf6873c5SCy Schubert  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
29*bf6873c5SCy Schubert  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30*bf6873c5SCy Schubert  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31*bf6873c5SCy Schubert  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32*bf6873c5SCy Schubert  * DEALINGS IN THE SOFTWARE.
33*bf6873c5SCy Schubert  *
34*bf6873c5SCy Schubert  * SPDX-License-Identifier: MIT
35*bf6873c5SCy Schubert  */
36*bf6873c5SCy Schubert 
37*bf6873c5SCy Schubert #include <config.h>
38*bf6873c5SCy Schubert #include <portable/pam.h>
39*bf6873c5SCy Schubert #include <portable/system.h>
40*bf6873c5SCy Schubert 
41*bf6873c5SCy Schubert #include <tests/fakepam/pam.h>
42*bf6873c5SCy Schubert 
43*bf6873c5SCy Schubert /* Used for unused parameters to silence gcc warnings. */
44*bf6873c5SCy Schubert #define UNUSED __attribute__((__unused__))
45*bf6873c5SCy Schubert 
46*bf6873c5SCy Schubert 
47*bf6873c5SCy Schubert /*
48*bf6873c5SCy Schubert  * Return a stored PAM data element in the provided data variable.  As a
49*bf6873c5SCy Schubert  * special case, if the data is NULL, pretend it doesn't exist.
50*bf6873c5SCy Schubert  */
51*bf6873c5SCy Schubert int
pam_get_data(const pam_handle_t * pamh,const char * name,const void ** data)52*bf6873c5SCy Schubert pam_get_data(const pam_handle_t *pamh, const char *name, const void **data)
53*bf6873c5SCy Schubert {
54*bf6873c5SCy Schubert     struct fakepam_data *item;
55*bf6873c5SCy Schubert 
56*bf6873c5SCy Schubert     for (item = pamh->data; item != NULL; item = item->next)
57*bf6873c5SCy Schubert         if (strcmp(item->name, name) == 0) {
58*bf6873c5SCy Schubert             if (item->data == NULL)
59*bf6873c5SCy Schubert                 return PAM_NO_MODULE_DATA;
60*bf6873c5SCy Schubert             *data = item->data;
61*bf6873c5SCy Schubert             return PAM_SUCCESS;
62*bf6873c5SCy Schubert         }
63*bf6873c5SCy Schubert     return PAM_NO_MODULE_DATA;
64*bf6873c5SCy Schubert }
65*bf6873c5SCy Schubert 
66*bf6873c5SCy Schubert 
67*bf6873c5SCy Schubert /*
68*bf6873c5SCy Schubert  * Store a data item.  Replaces the existing data item (calling its cleanup)
69*bf6873c5SCy Schubert  * if it is already set; otherwise, add a new data item.
70*bf6873c5SCy Schubert  */
71*bf6873c5SCy Schubert int
pam_set_data(pam_handle_t * pamh,const char * item,void * data,void (* cleanup)(pam_handle_t *,void *,int))72*bf6873c5SCy Schubert pam_set_data(pam_handle_t *pamh, const char *item, void *data,
73*bf6873c5SCy Schubert              void (*cleanup)(pam_handle_t *, void *, int))
74*bf6873c5SCy Schubert {
75*bf6873c5SCy Schubert     struct fakepam_data *p;
76*bf6873c5SCy Schubert 
77*bf6873c5SCy Schubert     for (p = pamh->data; p != NULL; p = p->next)
78*bf6873c5SCy Schubert         if (strcmp(p->name, item) == 0) {
79*bf6873c5SCy Schubert             if (p->cleanup != NULL)
80*bf6873c5SCy Schubert                 p->cleanup(pamh, p->data, PAM_DATA_REPLACE);
81*bf6873c5SCy Schubert             p->data = data;
82*bf6873c5SCy Schubert             p->cleanup = cleanup;
83*bf6873c5SCy Schubert             return PAM_SUCCESS;
84*bf6873c5SCy Schubert         }
85*bf6873c5SCy Schubert     p = malloc(sizeof(struct fakepam_data));
86*bf6873c5SCy Schubert     if (p == NULL)
87*bf6873c5SCy Schubert         return PAM_BUF_ERR;
88*bf6873c5SCy Schubert     p->name = strdup(item);
89*bf6873c5SCy Schubert     if (p->name == NULL) {
90*bf6873c5SCy Schubert         free(p);
91*bf6873c5SCy Schubert         return PAM_BUF_ERR;
92*bf6873c5SCy Schubert     }
93*bf6873c5SCy Schubert     p->data = data;
94*bf6873c5SCy Schubert     p->cleanup = cleanup;
95*bf6873c5SCy Schubert     p->next = pamh->data;
96*bf6873c5SCy Schubert     pamh->data = p;
97*bf6873c5SCy Schubert     return PAM_SUCCESS;
98*bf6873c5SCy Schubert }
99*bf6873c5SCy Schubert 
100*bf6873c5SCy Schubert 
101*bf6873c5SCy Schubert /*
102*bf6873c5SCy Schubert  * Retrieve a PAM item.  Currently, this only supports a limited subset of the
103*bf6873c5SCy Schubert  * possible items.
104*bf6873c5SCy Schubert  */
105*bf6873c5SCy Schubert int
pam_get_item(const pam_handle_t * pamh,int item,PAM_CONST void ** data)106*bf6873c5SCy Schubert pam_get_item(const pam_handle_t *pamh, int item, PAM_CONST void **data)
107*bf6873c5SCy Schubert {
108*bf6873c5SCy Schubert     switch (item) {
109*bf6873c5SCy Schubert     case PAM_AUTHTOK:
110*bf6873c5SCy Schubert         *data = pamh->authtok;
111*bf6873c5SCy Schubert         return PAM_SUCCESS;
112*bf6873c5SCy Schubert     case PAM_CONV:
113*bf6873c5SCy Schubert         if (pamh->conversation) {
114*bf6873c5SCy Schubert             *data = pamh->conversation;
115*bf6873c5SCy Schubert             return PAM_SUCCESS;
116*bf6873c5SCy Schubert         } else {
117*bf6873c5SCy Schubert             return PAM_BAD_ITEM;
118*bf6873c5SCy Schubert         }
119*bf6873c5SCy Schubert     case PAM_OLDAUTHTOK:
120*bf6873c5SCy Schubert         *data = pamh->oldauthtok;
121*bf6873c5SCy Schubert         return PAM_SUCCESS;
122*bf6873c5SCy Schubert     case PAM_RHOST:
123*bf6873c5SCy Schubert         *data = (PAM_CONST char *) pamh->rhost;
124*bf6873c5SCy Schubert         return PAM_SUCCESS;
125*bf6873c5SCy Schubert     case PAM_RUSER:
126*bf6873c5SCy Schubert         *data = (PAM_CONST char *) pamh->ruser;
127*bf6873c5SCy Schubert         return PAM_SUCCESS;
128*bf6873c5SCy Schubert     case PAM_SERVICE:
129*bf6873c5SCy Schubert         *data = (PAM_CONST char *) pamh->service;
130*bf6873c5SCy Schubert         return PAM_SUCCESS;
131*bf6873c5SCy Schubert     case PAM_TTY:
132*bf6873c5SCy Schubert         *data = (PAM_CONST char *) pamh->tty;
133*bf6873c5SCy Schubert         return PAM_SUCCESS;
134*bf6873c5SCy Schubert     case PAM_USER:
135*bf6873c5SCy Schubert         *data = (PAM_CONST char *) pamh->user;
136*bf6873c5SCy Schubert         return PAM_SUCCESS;
137*bf6873c5SCy Schubert     case PAM_USER_PROMPT:
138*bf6873c5SCy Schubert         *data = "login: ";
139*bf6873c5SCy Schubert         return PAM_SUCCESS;
140*bf6873c5SCy Schubert     default:
141*bf6873c5SCy Schubert         return PAM_BAD_ITEM;
142*bf6873c5SCy Schubert     }
143*bf6873c5SCy Schubert }
144*bf6873c5SCy Schubert 
145*bf6873c5SCy Schubert 
146*bf6873c5SCy Schubert /*
147*bf6873c5SCy Schubert  * Set a PAM item.  Currently only PAM_USER is supported.
148*bf6873c5SCy Schubert  */
149*bf6873c5SCy Schubert int
pam_set_item(pam_handle_t * pamh,int item,PAM_CONST void * data)150*bf6873c5SCy Schubert pam_set_item(pam_handle_t *pamh, int item, PAM_CONST void *data)
151*bf6873c5SCy Schubert {
152*bf6873c5SCy Schubert     switch (item) {
153*bf6873c5SCy Schubert     case PAM_AUTHTOK:
154*bf6873c5SCy Schubert         free(pamh->authtok);
155*bf6873c5SCy Schubert         pamh->authtok = strdup(data);
156*bf6873c5SCy Schubert         if (pamh->authtok == NULL)
157*bf6873c5SCy Schubert             return PAM_BUF_ERR;
158*bf6873c5SCy Schubert         return PAM_SUCCESS;
159*bf6873c5SCy Schubert     case PAM_OLDAUTHTOK:
160*bf6873c5SCy Schubert         free(pamh->oldauthtok);
161*bf6873c5SCy Schubert         pamh->oldauthtok = strdup(data);
162*bf6873c5SCy Schubert         if (pamh->oldauthtok == NULL)
163*bf6873c5SCy Schubert             return PAM_BUF_ERR;
164*bf6873c5SCy Schubert         return PAM_SUCCESS;
165*bf6873c5SCy Schubert     case PAM_RHOST:
166*bf6873c5SCy Schubert         free(pamh->rhost);
167*bf6873c5SCy Schubert         pamh->rhost = strdup(data);
168*bf6873c5SCy Schubert         if (pamh->rhost == NULL)
169*bf6873c5SCy Schubert             return PAM_BUF_ERR;
170*bf6873c5SCy Schubert         return PAM_SUCCESS;
171*bf6873c5SCy Schubert     case PAM_RUSER:
172*bf6873c5SCy Schubert         free(pamh->ruser);
173*bf6873c5SCy Schubert         pamh->ruser = strdup(data);
174*bf6873c5SCy Schubert         if (pamh->ruser == NULL)
175*bf6873c5SCy Schubert             return PAM_BUF_ERR;
176*bf6873c5SCy Schubert         return PAM_SUCCESS;
177*bf6873c5SCy Schubert     case PAM_TTY:
178*bf6873c5SCy Schubert         free(pamh->tty);
179*bf6873c5SCy Schubert         pamh->tty = strdup(data);
180*bf6873c5SCy Schubert         if (pamh->tty == NULL)
181*bf6873c5SCy Schubert             return PAM_BUF_ERR;
182*bf6873c5SCy Schubert         return PAM_SUCCESS;
183*bf6873c5SCy Schubert     case PAM_USER:
184*bf6873c5SCy Schubert         pamh->user = (const char *) data;
185*bf6873c5SCy Schubert         return PAM_SUCCESS;
186*bf6873c5SCy Schubert     default:
187*bf6873c5SCy Schubert         return PAM_BAD_ITEM;
188*bf6873c5SCy Schubert     }
189*bf6873c5SCy Schubert }
190*bf6873c5SCy Schubert 
191*bf6873c5SCy Schubert 
192*bf6873c5SCy Schubert /*
193*bf6873c5SCy Schubert  * Return the user for the PAM context.
194*bf6873c5SCy Schubert  */
195*bf6873c5SCy Schubert int
pam_get_user(pam_handle_t * pamh,PAM_CONST char ** user,const char * prompt UNUSED)196*bf6873c5SCy Schubert pam_get_user(pam_handle_t *pamh, PAM_CONST char **user,
197*bf6873c5SCy Schubert              const char *prompt UNUSED)
198*bf6873c5SCy Schubert {
199*bf6873c5SCy Schubert     if (pamh->user == NULL)
200*bf6873c5SCy Schubert         return PAM_CONV_ERR;
201*bf6873c5SCy Schubert     else {
202*bf6873c5SCy Schubert         *user = (PAM_CONST char *) pamh->user;
203*bf6873c5SCy Schubert         return PAM_SUCCESS;
204*bf6873c5SCy Schubert     }
205*bf6873c5SCy Schubert }
206*bf6873c5SCy Schubert 
207*bf6873c5SCy Schubert 
208*bf6873c5SCy Schubert /*
209*bf6873c5SCy Schubert  * Return a setting in the PAM environment.
210*bf6873c5SCy Schubert  */
211*bf6873c5SCy Schubert PAM_CONST char *
pam_getenv(pam_handle_t * pamh,const char * name)212*bf6873c5SCy Schubert pam_getenv(pam_handle_t *pamh, const char *name)
213*bf6873c5SCy Schubert {
214*bf6873c5SCy Schubert     size_t i;
215*bf6873c5SCy Schubert 
216*bf6873c5SCy Schubert     if (pamh->environ == NULL)
217*bf6873c5SCy Schubert         return NULL;
218*bf6873c5SCy Schubert     for (i = 0; pamh->environ[i] != NULL; i++)
219*bf6873c5SCy Schubert         if (strncmp(name, pamh->environ[i], strlen(name)) == 0
220*bf6873c5SCy Schubert             && pamh->environ[i][strlen(name)] == '=')
221*bf6873c5SCy Schubert             return pamh->environ[i] + strlen(name) + 1;
222*bf6873c5SCy Schubert     return NULL;
223*bf6873c5SCy Schubert }
224*bf6873c5SCy Schubert 
225*bf6873c5SCy Schubert 
226*bf6873c5SCy Schubert /*
227*bf6873c5SCy Schubert  * Return a newly malloc'd copy of the complete PAM environment.  This must be
228*bf6873c5SCy Schubert  * freed by the caller.
229*bf6873c5SCy Schubert  */
230*bf6873c5SCy Schubert char **
pam_getenvlist(pam_handle_t * pamh)231*bf6873c5SCy Schubert pam_getenvlist(pam_handle_t *pamh)
232*bf6873c5SCy Schubert {
233*bf6873c5SCy Schubert     char **env;
234*bf6873c5SCy Schubert     size_t i;
235*bf6873c5SCy Schubert 
236*bf6873c5SCy Schubert     if (pamh->environ == NULL) {
237*bf6873c5SCy Schubert         pamh->environ = malloc(sizeof(char *));
238*bf6873c5SCy Schubert         if (pamh->environ == NULL)
239*bf6873c5SCy Schubert             return NULL;
240*bf6873c5SCy Schubert         pamh->environ[0] = NULL;
241*bf6873c5SCy Schubert     }
242*bf6873c5SCy Schubert     for (i = 0; pamh->environ[i] != NULL; i++)
243*bf6873c5SCy Schubert         ;
244*bf6873c5SCy Schubert     env = calloc(i + 1, sizeof(char *));
245*bf6873c5SCy Schubert     if (env == NULL)
246*bf6873c5SCy Schubert         return NULL;
247*bf6873c5SCy Schubert     for (i = 0; pamh->environ[i] != NULL; i++) {
248*bf6873c5SCy Schubert         env[i] = strdup(pamh->environ[i]);
249*bf6873c5SCy Schubert         if (env[i] == NULL)
250*bf6873c5SCy Schubert             goto fail;
251*bf6873c5SCy Schubert     }
252*bf6873c5SCy Schubert     env[i] = NULL;
253*bf6873c5SCy Schubert     return env;
254*bf6873c5SCy Schubert 
255*bf6873c5SCy Schubert fail:
256*bf6873c5SCy Schubert     for (i = 0; env[i] != NULL; i++)
257*bf6873c5SCy Schubert         free(env[i]);
258*bf6873c5SCy Schubert     free(env);
259*bf6873c5SCy Schubert     return NULL;
260*bf6873c5SCy Schubert }
261*bf6873c5SCy Schubert 
262*bf6873c5SCy Schubert 
263*bf6873c5SCy Schubert /*
264*bf6873c5SCy Schubert  * Add a setting to the PAM environment.  If there is another existing
265*bf6873c5SCy Schubert  * variable with the same value, the value is replaced, unless the setting
266*bf6873c5SCy Schubert  * doesn't end in an equal sign.  If it doesn't end in an equal sign, any
267*bf6873c5SCy Schubert  * existing environment variable of that name is removed.  This follows the
268*bf6873c5SCy Schubert  * Linux PAM semantics.
269*bf6873c5SCy Schubert  *
270*bf6873c5SCy Schubert  * On HP-UX, there is no separate PAM environment, so the module just uses the
271*bf6873c5SCy Schubert  * main environment.  For our tests to work on that platform, we therefore
272*bf6873c5SCy Schubert  * have to do the same thing.
273*bf6873c5SCy Schubert  */
274*bf6873c5SCy Schubert #ifdef HAVE_PAM_GETENV
275*bf6873c5SCy Schubert int
pam_putenv(pam_handle_t * pamh,const char * setting)276*bf6873c5SCy Schubert pam_putenv(pam_handle_t *pamh, const char *setting)
277*bf6873c5SCy Schubert {
278*bf6873c5SCy Schubert     char *copy = NULL;
279*bf6873c5SCy Schubert     const char *equals;
280*bf6873c5SCy Schubert     size_t namelen;
281*bf6873c5SCy Schubert     bool delete = false;
282*bf6873c5SCy Schubert     bool found = false;
283*bf6873c5SCy Schubert     size_t i, j;
284*bf6873c5SCy Schubert     char **env;
285*bf6873c5SCy Schubert 
286*bf6873c5SCy Schubert     equals = strchr(setting, '=');
287*bf6873c5SCy Schubert     if (equals != NULL)
288*bf6873c5SCy Schubert         namelen = equals - setting;
289*bf6873c5SCy Schubert     else {
290*bf6873c5SCy Schubert         delete = true;
291*bf6873c5SCy Schubert         namelen = strlen(setting);
292*bf6873c5SCy Schubert     }
293*bf6873c5SCy Schubert     if (!delete) {
294*bf6873c5SCy Schubert         copy = strdup(setting);
295*bf6873c5SCy Schubert         if (copy == NULL)
296*bf6873c5SCy Schubert             return PAM_BUF_ERR;
297*bf6873c5SCy Schubert     }
298*bf6873c5SCy Schubert 
299*bf6873c5SCy Schubert     /* Handle the first call to pam_putenv. */
300*bf6873c5SCy Schubert     if (pamh->environ == NULL) {
301*bf6873c5SCy Schubert         if (delete)
302*bf6873c5SCy Schubert             return PAM_BAD_ITEM;
303*bf6873c5SCy Schubert         pamh->environ = calloc(2, sizeof(char *));
304*bf6873c5SCy Schubert         if (pamh->environ == NULL) {
305*bf6873c5SCy Schubert             free(copy);
306*bf6873c5SCy Schubert             return PAM_BUF_ERR;
307*bf6873c5SCy Schubert         }
308*bf6873c5SCy Schubert         pamh->environ[0] = copy;
309*bf6873c5SCy Schubert         pamh->environ[1] = NULL;
310*bf6873c5SCy Schubert         return PAM_SUCCESS;
311*bf6873c5SCy Schubert     }
312*bf6873c5SCy Schubert 
313*bf6873c5SCy Schubert     /*
314*bf6873c5SCy Schubert      * We have an existing array.  See if we're replacing a value, deleting a
315*bf6873c5SCy Schubert      * value, or adding a new one.  When deleting, waste a bit of memory but
316*bf6873c5SCy Schubert      * save some time by not bothering to reduce the size of the array.
317*bf6873c5SCy Schubert      */
318*bf6873c5SCy Schubert     for (i = 0; pamh->environ[i] != NULL; i++)
319*bf6873c5SCy Schubert         if (strncmp(setting, pamh->environ[i], namelen) == 0
320*bf6873c5SCy Schubert             && pamh->environ[i][namelen] == '=') {
321*bf6873c5SCy Schubert             if (delete) {
322*bf6873c5SCy Schubert                 free(pamh->environ[i]);
323*bf6873c5SCy Schubert                 for (j = i + 1; pamh->environ[j] != NULL; i++, j++)
324*bf6873c5SCy Schubert                     pamh->environ[i] = pamh->environ[j];
325*bf6873c5SCy Schubert                 pamh->environ[i] = NULL;
326*bf6873c5SCy Schubert             } else {
327*bf6873c5SCy Schubert                 free(pamh->environ[i]);
328*bf6873c5SCy Schubert                 pamh->environ[i] = copy;
329*bf6873c5SCy Schubert             }
330*bf6873c5SCy Schubert             found = true;
331*bf6873c5SCy Schubert             break;
332*bf6873c5SCy Schubert         }
333*bf6873c5SCy Schubert     if (!found) {
334*bf6873c5SCy Schubert         if (delete)
335*bf6873c5SCy Schubert             return PAM_BAD_ITEM;
336*bf6873c5SCy Schubert         env = reallocarray(pamh->environ, (i + 2), sizeof(char *));
337*bf6873c5SCy Schubert         if (env == NULL) {
338*bf6873c5SCy Schubert             free(copy);
339*bf6873c5SCy Schubert             return PAM_BUF_ERR;
340*bf6873c5SCy Schubert         }
341*bf6873c5SCy Schubert         pamh->environ = env;
342*bf6873c5SCy Schubert         pamh->environ[i] = copy;
343*bf6873c5SCy Schubert         pamh->environ[i + 1] = NULL;
344*bf6873c5SCy Schubert     }
345*bf6873c5SCy Schubert     return PAM_SUCCESS;
346*bf6873c5SCy Schubert }
347*bf6873c5SCy Schubert 
348*bf6873c5SCy Schubert #else /* !HAVE_PAM_GETENV */
349*bf6873c5SCy Schubert 
350*bf6873c5SCy Schubert int
pam_putenv(pam_handle_t * pamh UNUSED,const char * setting)351*bf6873c5SCy Schubert pam_putenv(pam_handle_t *pamh UNUSED, const char *setting)
352*bf6873c5SCy Schubert {
353*bf6873c5SCy Schubert     return putenv((char *) setting);
354*bf6873c5SCy Schubert }
355*bf6873c5SCy Schubert 
356*bf6873c5SCy Schubert #endif /* !HAVE_PAM_GETENV */
357