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