]> granicus.if.org Git - linux-pam/blob - libpam/pam_env.c
Relevant BUGIDs: 565470
[linux-pam] / libpam / pam_env.c
1 /*
2  * pam_env.c
3  *
4  * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996,1997
5  * All rights reserved.
6  *
7  * This file was written from a "hint" provided by the people at SUN.
8  * and the X/Open XSSO draft of March 1997.
9  *
10  * $Id$
11  */
12
13 #include <string.h>
14 #include <stdlib.h>
15 #ifdef sunos
16 #define memmove(x,y,z) bcopy(y,x,z)
17 #endif
18
19 #include "pam_private.h"
20
21 /* helper functions */
22
23 #ifdef DEBUG
24 static void _pam_dump_env(pam_handle_t *pamh)
25 {
26     int i;
27
28     D(("Listing environment of pamh=%p", pamh));
29     D(("pamh->env = %p", pamh->env));
30     D(("environment entries used = %d [of %d allocated]"
31        , pamh->env->requested, pamh->env->entries));
32
33     for (i=0; i<pamh->env->requested; ++i) {
34         _pam_output_debug(">%-3d [%9p]:[%s]"
35                           , i, pamh->env->list[i], pamh->env->list[i]);
36     }
37     _pam_output_debug("*NOTE* the last item should be (nil)");
38 }
39 #else
40 #define _pam_dump_env(x)
41 #endif
42
43 /*
44  * Create the environment
45  */
46
47 int _pam_make_env(pam_handle_t *pamh)
48 {
49     D(("called."));
50
51     IF_NO_PAMH("_pam_make_env", pamh, PAM_ABORT);
52
53     /*
54      * get structure memory
55      */
56
57     pamh->env = (struct pam_environ *) malloc(sizeof(struct pam_environ));
58     if (pamh->env == NULL) {
59         _pam_system_log(LOG_CRIT, "_pam_make_env: out of memory");
60         return PAM_BUF_ERR;
61     }
62
63     /*
64      * get list memory
65      */
66
67     pamh->env->list = (char **)calloc( PAM_ENV_CHUNK, sizeof(char *) );
68     if (pamh->env->list == NULL) {
69         _pam_system_log(LOG_CRIT, "_pam_make_env: no memory for list");
70         _pam_drop(pamh->env);
71         return PAM_BUF_ERR;
72     }
73
74     /*
75      * fill entries in pamh->env
76      */
77     
78     pamh->env->entries = PAM_ENV_CHUNK;
79     pamh->env->requested = 1;
80     pamh->env->list[0] = NULL;
81
82     _pam_dump_env(pamh);                    /* only active when debugging */
83
84     return PAM_SUCCESS;
85 }
86
87 /*
88  * purge the environment
89  */
90
91 void _pam_drop_env(pam_handle_t *pamh)
92 {
93     D(("called."));
94     IF_NO_PAMH("_pam_make_env", pamh, /* nothing to return */);
95
96     if (pamh->env != NULL) {
97         int i;
98         /* we will only purge the pamh->env->requested number of elements */
99
100         for (i=pamh->env->requested-1; i-- > 0; ) {
101             D(("dropping #%3d>%s<", i, pamh->env->list[i]));
102             _pam_overwrite(pamh->env->list[i]);          /* clean */
103             _pam_drop(pamh->env->list[i]);              /* forget */
104         }
105         pamh->env->requested = 0;
106         pamh->env->entries = 0;
107         _pam_drop(pamh->env->list);                     /* forget */
108         _pam_drop(pamh->env);                           /* forget */
109     } else {
110         D(("no environment present in pamh?"));
111     }
112 }
113
114 /*
115  * Return the item number of the given variable = first 'length' chars
116  * of 'name_value'. Since this is a static function, it is safe to
117  * assume its supplied arguments are well defined.
118  */
119
120 static int _pam_search_env(const struct pam_environ *env
121                            , const char *name_value, int length)
122 {
123     int i;
124
125     for (i=env->requested-1; i-- > 0; ) {
126         if (strncmp(name_value,env->list[i],length) == 0
127             && env->list[i][length] == '=') {
128
129             return i;                                   /* Got it! */
130
131         }
132     }
133
134     return -1;                                          /* no luck */
135 }
136
137 /*
138  * externally visible functions
139  */
140
141 /*
142  *  pam_putenv(): Add/replace/delete a PAM-environment variable.
143  *
144  *  Add/replace:
145  *      name_value = "NAME=VALUE" or "NAME=" (for empty value="\0")
146  *
147  *  delete:
148  *      name_value = "NAME"
149  */
150
151 int pam_putenv(pam_handle_t *pamh, const char *name_value)
152 {
153     int l2eq, item, retval;
154
155     D(("called."));
156     IF_NO_PAMH("pam_putenv", pamh, PAM_ABORT);
157
158     if (name_value == NULL) {
159         _pam_system_log(LOG_ERR, "pam_putenv: no variable indicated");
160         return PAM_PERM_DENIED;
161     }
162
163     /*
164      * establish if we are setting or deleting; scan for '='
165      */
166
167     for (l2eq=0; name_value[l2eq] && name_value[l2eq] != '='; ++l2eq);
168     if (l2eq <= 0) {
169         _pam_system_log(LOG_ERR, "pam_putenv: bad variable");
170         return PAM_BAD_ITEM;
171     }
172
173     /*
174      *  Look first for environment.
175      */
176
177     if (pamh->env == NULL || pamh->env->list == NULL) {
178         _pam_system_log(LOG_ERR, "pam_putenv: no env%s found",
179                        pamh->env == NULL ? "":"-list");
180         return PAM_ABORT;
181     }
182
183     /* find the item to replace */
184
185     item = _pam_search_env(pamh->env, name_value, l2eq);
186
187     if (name_value[l2eq]) {                     /* (re)setting */
188
189         if (item == -1) {                      /* new variable */
190             D(("adding item: %s", name_value));
191             /* enough space? */
192             if (pamh->env->entries <= pamh->env->requested) {
193                 register int i;
194                 register char **tmp;
195
196                 /* get some new space */
197                 tmp = calloc( pamh->env->entries + PAM_ENV_CHUNK
198                                      , sizeof(char *) );
199                 if (tmp == NULL) {
200                     /* nothing has changed - old env intact */
201                     _pam_system_log(LOG_CRIT,
202                                     "pam_putenv: cannot grow environment");
203                     return PAM_BUF_ERR;
204                 }
205
206                 /* copy old env-item pointers/forget old */
207                 for (i=0; i<pamh->env->requested; ++i) {
208                     tmp[i] = pamh->env->list[i];
209                     pamh->env->list[i] = NULL;
210                 }
211
212                 /* drop old list and replace with new */
213                 _pam_drop(pamh->env->list);
214                 pamh->env->list = tmp;
215                 pamh->env->entries += PAM_ENV_CHUNK;
216
217                 D(("resized env list"));
218                 _pam_dump_env(pamh);              /* only when debugging */
219             }
220
221             item = pamh->env->requested-1;        /* old last item (NULL) */
222
223             /* add a new NULL entry at end; increase counter */
224             pamh->env->list[pamh->env->requested++] = NULL;
225             
226         } else {                                /* replace old */
227             D(("replacing item: %s\n          with: %s"
228                , pamh->env->list[item], name_value));
229             _pam_overwrite(pamh->env->list[item]);
230             _pam_drop(pamh->env->list[item]);
231         }
232
233         /*
234          * now we have a place to put the new env-item, insert at 'item'
235          */
236
237         pamh->env->list[item] = _pam_strdup(name_value);
238         if (pamh->env->list[item] != NULL) {
239             _pam_dump_env(pamh);                   /* only when debugging */
240             return PAM_SUCCESS;
241         }
242
243         /* something went wrong; we should delete the item - fall through */
244
245         retval = PAM_BUF_ERR;                        /* an error occurred */
246     } else {
247         retval = PAM_SUCCESS;                      /* we requested delete */
248     }
249
250     /* getting to here implies we are deleting an item */
251
252     if (item < 0) {
253         _pam_system_log(LOG_ERR, "pam_putenv: delete non-existent entry; %s",
254                        name_value);
255         return PAM_BAD_ITEM;
256     }
257
258     /*
259      * remove item: purge memory; reset counter; resize [; display-env]
260      */
261
262     D(("deleting: env#%3d:[%s]", item, pamh->env->list[item]));
263     _pam_overwrite(pamh->env->list[item]);
264     _pam_drop(pamh->env->list[item]);
265     --(pamh->env->requested);
266     D(("mmove: item[%d]+%d -> item[%d]"
267        , item+1, ( pamh->env->requested - item ), item));
268     (void) memmove(&pamh->env->list[item], &pamh->env->list[item+1]
269                    , ( pamh->env->requested - item )*sizeof(char *) );
270
271     _pam_dump_env(pamh);                   /* only when debugging */
272
273     /*
274      * deleted.
275      */
276
277     return retval;
278 }
279
280 /*
281  *  Return the value of the requested environment variable
282  */
283
284 const char *pam_getenv(pam_handle_t *pamh, const char *name)
285 {
286     int item;
287
288     D(("called."));
289     IF_NO_PAMH("pam_getenv", pamh, NULL);
290
291     if (name == NULL) {
292         _pam_system_log(LOG_ERR, "pam_getenv: no variable indicated");
293         return NULL;
294     }
295
296     if (pamh->env == NULL || pamh->env->list == NULL) {
297         _pam_system_log(LOG_ERR, "pam_getenv: no env%s found",
298                         pamh->env == NULL ? "":"-list" );
299         return NULL;
300     }
301
302     /* find the requested item */
303
304     item = _pam_search_env(pamh->env, name, strlen(name));
305     if (item != -1) {
306
307         D(("env-item: %s, found!", name));
308         return (pamh->env->list[item] + 1 + strlen(name));
309
310     } else {
311
312         D(("env-item: %s, not found", name));
313         return NULL;
314
315     }
316 }
317
318 static char **_copy_env(pam_handle_t *pamh)
319 {
320     char **dump;
321     int i = pamh->env->requested;          /* reckon size of environment */
322     char *const *env = pamh->env->list;
323
324     D(("now get some memory for dump"));
325
326     /* allocate some memory for this (plus the null tail-pointer) */
327     dump = (char **) calloc(i, sizeof(char *));
328     D(("dump = %p", dump));
329     if (dump == NULL) {
330         return NULL;
331     }
332
333     /* now run through entries and copy the variables over */
334     dump[--i] = NULL;
335     while (i-- > 0) {
336         D(("env[%d]=`%s'", i,env[i]));
337         dump[i] = _pam_strdup(env[i]);
338         D(("->dump[%d]=`%s'", i,dump[i]));
339         if (dump[i] == NULL) {
340             /* out of memory */
341
342             while (dump[++i]) {
343                 _pam_overwrite(dump[i]);
344                 _pam_drop(dump[i]);
345             }
346             return NULL;
347         }
348     }
349
350     env = NULL;                             /* forget now */
351
352     /* return transcribed environment */
353     return dump;
354 }
355
356 char **pam_getenvlist(pam_handle_t *pamh)
357 {
358     int i;
359
360     D(("called."));
361     IF_NO_PAMH("pam_getenvlist", pamh, NULL);
362
363     if (pamh->env == NULL || pamh->env->list == NULL) {
364         _pam_system_log(LOG_ERR, "pam_getenvlist: no env%s found",
365                         pamh->env == NULL ? "":"-list" );
366         return NULL;
367     }
368
369     /* some quick checks */
370
371     if (pamh->env->requested > pamh->env->entries) {
372         _pam_system_log(LOG_ERR, "pam_getenvlist: environment corruption");
373         _pam_dump_env(pamh);                 /* only active when debugging */
374         return NULL;
375     }
376
377     for (i=pamh->env->requested-1; i-- > 0; ) {
378         if (pamh->env->list[i] == NULL) {
379             _pam_system_log(LOG_ERR, "pam_getenvlist: environment broken");
380             _pam_dump_env(pamh);              /* only active when debugging */
381             return NULL;          /* somehow we've broken the environment!? */
382         }
383     }
384
385     /* Seems fine; copy environment */
386
387     _pam_dump_env(pamh);                    /* only active when debugging */
388
389     return _copy_env(pamh);
390 }