1*39339e78Sderaadt /* $OpenBSD: env.c,v 1.34 2022/05/21 01:21:29 deraadt Exp $ */
2e134e629Smillert
3df930be7Sderaadt /* Copyright 1988,1990,1993,1994 by Paul Vixie
4a5198fa1Smillert * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5f454ebdeSmillert * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
6df930be7Sderaadt *
7f454ebdeSmillert * Permission to use, copy, modify, and distribute this software for any
8f454ebdeSmillert * purpose with or without fee is hereby granted, provided that the above
9f454ebdeSmillert * copyright notice and this permission notice appear in all copies.
10df930be7Sderaadt *
11a5198fa1Smillert * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12a5198fa1Smillert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a5198fa1Smillert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14a5198fa1Smillert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a5198fa1Smillert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a5198fa1Smillert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17a5198fa1Smillert * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18df930be7Sderaadt */
19df930be7Sderaadt
20fa575ea2Smillert #include <sys/types.h>
21fa575ea2Smillert
22fa575ea2Smillert #include <bitstring.h> /* for structs.h */
23fa575ea2Smillert #include <ctype.h>
24fa575ea2Smillert #include <errno.h>
25fa575ea2Smillert #include <stdio.h>
26fa575ea2Smillert #include <stdlib.h>
27fa575ea2Smillert #include <string.h>
28fa575ea2Smillert #include <time.h> /* for structs.h */
29fa575ea2Smillert
30fa575ea2Smillert #include "macros.h"
31fa575ea2Smillert #include "structs.h"
32fa575ea2Smillert #include "funcs.h"
33fa575ea2Smillert #include "globals.h"
34df930be7Sderaadt
35df930be7Sderaadt char **
env_init(void)36785ecd14Stedu env_init(void)
37785ecd14Stedu {
383d6ea532Smillert char **p = malloc(sizeof(char *));
39df930be7Sderaadt
40f454ebdeSmillert if (p != NULL)
41df930be7Sderaadt p[0] = NULL;
42df930be7Sderaadt return (p);
43df930be7Sderaadt }
44df930be7Sderaadt
45df930be7Sderaadt void
env_free(char ** envp)46785ecd14Stedu env_free(char **envp)
47785ecd14Stedu {
48df930be7Sderaadt char **p;
49df930be7Sderaadt
50c95ed473Smillert for (p = envp; *p != NULL; p++)
51df930be7Sderaadt free(*p);
52df930be7Sderaadt free(envp);
53df930be7Sderaadt }
54df930be7Sderaadt
55df930be7Sderaadt char **
env_copy(char ** envp)56785ecd14Stedu env_copy(char **envp)
57785ecd14Stedu {
58f454ebdeSmillert int count, i, save_errno;
59f454ebdeSmillert char **p;
60df930be7Sderaadt
61df930be7Sderaadt for (count = 0; envp[count] != NULL; count++)
62e684b8a0Smillert continue;
63c1606dc6Smillert p = reallocarray(NULL, count+1, sizeof(char *)); /* 1 for the NULL */
64f454ebdeSmillert if (p != NULL) {
65df930be7Sderaadt for (i = 0; i < count; i++)
66bac4f837Smillert if ((p[i] = strdup(envp[i])) == NULL) {
67f454ebdeSmillert save_errno = errno;
68bac4f837Smillert while (--i >= 0)
69f454ebdeSmillert free(p[i]);
70bac4f837Smillert free(p);
71f454ebdeSmillert errno = save_errno;
72f454ebdeSmillert return (NULL);
73bac4f837Smillert }
74df930be7Sderaadt p[count] = NULL;
75f454ebdeSmillert }
76df930be7Sderaadt return (p);
77df930be7Sderaadt }
78df930be7Sderaadt
793d6ea532Smillert static char *
env_find(char * name,char ** envp,size_t * count)803d6ea532Smillert env_find(char *name, char **envp, size_t *count)
813d6ea532Smillert {
823d6ea532Smillert char **ep, *p, *q;
833d6ea532Smillert size_t len;
843d6ea532Smillert
853d6ea532Smillert /*
863d6ea532Smillert * Find name in envp and return its value along with the
873d6ea532Smillert * index it was found at or the length of envp if not found.
883d6ea532Smillert * We treat a '=' in name as end of string for env_set().
893d6ea532Smillert */
903d6ea532Smillert for (p = name; *p && *p != '='; p++)
913d6ea532Smillert continue;
923d6ea532Smillert len = (size_t)(p - name);
933d6ea532Smillert for (ep = envp; (p = *ep) != NULL; ep++) {
943d6ea532Smillert if ((q = strchr(p, '=')) == NULL)
953d6ea532Smillert continue;
963d6ea532Smillert if ((size_t)(q - p) == len && strncmp(p, name, len) == 0) {
973d6ea532Smillert p = q + 1;
983d6ea532Smillert break;
993d6ea532Smillert }
1003d6ea532Smillert }
1013d6ea532Smillert *count = (size_t)(ep - envp);
1023d6ea532Smillert return (p);
1033d6ea532Smillert }
1043d6ea532Smillert
1053d6ea532Smillert char *
env_get(char * name,char ** envp)1063d6ea532Smillert env_get(char *name, char **envp)
1073d6ea532Smillert {
1083d6ea532Smillert size_t count;
1093d6ea532Smillert
1103d6ea532Smillert return (env_find(name, envp, &count));
1113d6ea532Smillert }
1123d6ea532Smillert
113df930be7Sderaadt char **
env_set(char ** envp,char * envstr)114785ecd14Stedu env_set(char **envp, char *envstr)
115785ecd14Stedu {
11666ca85e4Smillert size_t count;
1173d6ea532Smillert char **p, *envcopy;
118df930be7Sderaadt
1193d6ea532Smillert if ((envcopy = strdup(envstr)) == NULL)
120f454ebdeSmillert return (NULL);
1213d6ea532Smillert
1223d6ea532Smillert /* Replace existing name if found. */
1233d6ea532Smillert if (env_find(envstr, envp, &count) != NULL) {
1243d6ea532Smillert free(envp[count]);
1253d6ea532Smillert envp[count] = envcopy;
126df930be7Sderaadt return (envp);
127df930be7Sderaadt }
128df930be7Sderaadt
1293d6ea532Smillert /* Realloc envp and append new variable. */
1303d6ea532Smillert p = reallocarray(envp, count + 2, sizeof(char **));
131d54c3a7cSmillert if (p == NULL) {
1323d6ea532Smillert free(envcopy);
133f454ebdeSmillert return (NULL);
134d54c3a7cSmillert }
1353d6ea532Smillert p[count++] = envcopy;
1363d6ea532Smillert p[count] = NULL;
137df930be7Sderaadt return (p);
138df930be7Sderaadt }
139df930be7Sderaadt
1402c7f28f2Smillert /* The following states are used by load_env(), traversed in order: */
1412c7f28f2Smillert enum env_state {
1422c7f28f2Smillert NAMEI, /* First char of NAME, may be quote */
1432c7f28f2Smillert NAME, /* Subsequent chars of NAME */
1442c7f28f2Smillert EQ1, /* After end of name, looking for '=' sign */
1452c7f28f2Smillert EQ2, /* After '=', skipping whitespace */
1462c7f28f2Smillert VALUEI, /* First char of VALUE, may be quote */
1472c7f28f2Smillert VALUE, /* Subsequent chars of VALUE */
1482c7f28f2Smillert FINI, /* All done, skipping trailing whitespace */
1492d2b3e00Sderaadt ERROR /* Error */
1502c7f28f2Smillert };
1512c7f28f2Smillert
152dd2fc19cStedu /* return -1 = end of file
153df930be7Sderaadt * FALSE = not an env setting (file was repositioned)
154df930be7Sderaadt * TRUE = was an env setting
155df930be7Sderaadt */
156df930be7Sderaadt int
load_env(char * envstr,FILE * f)157785ecd14Stedu load_env(char *envstr, FILE *f)
158785ecd14Stedu {
159df930be7Sderaadt long filepos;
160df930be7Sderaadt int fileline;
1612c7f28f2Smillert enum env_state state;
162d8ed82b3Sbitblt char name[MAX_ENVSTR], val[MAX_ENVSTR];
1632c7f28f2Smillert char quotechar, *c, *str;
164df930be7Sderaadt
165df930be7Sderaadt filepos = ftell(f);
166df930be7Sderaadt fileline = LineNumber;
167df930be7Sderaadt skip_comments(f);
168*39339e78Sderaadt if (get_string(envstr, MAX_ENVSTR, f, "\n") == EOF)
169dd2fc19cStedu return (-1);
170df930be7Sderaadt
1712c7f28f2Smillert bzero(name, sizeof name);
1722c7f28f2Smillert bzero(val, sizeof val);
1732c7f28f2Smillert str = name;
1742c7f28f2Smillert state = NAMEI;
1752c7f28f2Smillert quotechar = '\0';
1762c7f28f2Smillert c = envstr;
1772c7f28f2Smillert while (state != ERROR && *c) {
1782c7f28f2Smillert switch (state) {
1792c7f28f2Smillert case NAMEI:
1802c7f28f2Smillert case VALUEI:
1812c7f28f2Smillert if (*c == '\'' || *c == '"')
1822c7f28f2Smillert quotechar = *c++;
1832c7f28f2Smillert state++;
1842c7f28f2Smillert /* FALLTHROUGH */
1852c7f28f2Smillert case NAME:
1862c7f28f2Smillert case VALUE:
1872c7f28f2Smillert if (quotechar) {
1882c7f28f2Smillert if (*c == quotechar) {
1892c7f28f2Smillert state++;
1902c7f28f2Smillert c++;
1912c7f28f2Smillert break;
1922c7f28f2Smillert }
1932c7f28f2Smillert if (state == NAME && *c == '=') {
1942c7f28f2Smillert state = ERROR;
1952c7f28f2Smillert break;
1962c7f28f2Smillert }
1972c7f28f2Smillert } else {
1982c7f28f2Smillert if (state == NAME) {
1992c7f28f2Smillert if (isspace((unsigned char)*c)) {
2002c7f28f2Smillert c++;
2012c7f28f2Smillert state++;
2022c7f28f2Smillert break;
2032c7f28f2Smillert }
2042c7f28f2Smillert if (*c == '=') {
2052c7f28f2Smillert state++;
2062c7f28f2Smillert break;
2072c7f28f2Smillert }
2082c7f28f2Smillert }
2092c7f28f2Smillert }
2102c7f28f2Smillert *str++ = *c++;
2112c7f28f2Smillert break;
2122c7f28f2Smillert case EQ1:
2132c7f28f2Smillert if (*c == '=') {
2142c7f28f2Smillert state++;
2152c7f28f2Smillert str = val;
2162c7f28f2Smillert quotechar = '\0';
2172c7f28f2Smillert } else {
2182c7f28f2Smillert if (!isspace((unsigned char)*c))
2192c7f28f2Smillert state = ERROR;
2202c7f28f2Smillert }
2212c7f28f2Smillert c++;
2222c7f28f2Smillert break;
2232c7f28f2Smillert case EQ2:
2242c7f28f2Smillert case FINI:
2252c7f28f2Smillert if (isspace((unsigned char)*c))
2262c7f28f2Smillert c++;
2272c7f28f2Smillert else
2282c7f28f2Smillert state++;
2292c7f28f2Smillert break;
23095bc639aSmillert case ERROR:
23195bc639aSmillert /* handled below */
23295bc639aSmillert break;
2332c7f28f2Smillert }
2342c7f28f2Smillert }
235a9e807deSmillert if (state != FINI && !(state == VALUE && !quotechar))
236a9e807deSmillert goto not_env;
2372c7f28f2Smillert if (state == VALUE) {
2382c7f28f2Smillert /* End of unquoted value: trim trailing whitespace */
2392c7f28f2Smillert c = val + strlen(val);
2402c7f28f2Smillert while (c > val && isspace((unsigned char)c[-1]))
2412c7f28f2Smillert *(--c) = '\0';
2422c7f28f2Smillert }
243df930be7Sderaadt
2442c7f28f2Smillert /* 2 fields from parser; looks like an env setting */
245df930be7Sderaadt
246f454ebdeSmillert /*
247f454ebdeSmillert * This can't overflow because get_string() limited the size of the
2482c7f28f2Smillert * name and val fields. Still, it doesn't hurt to be careful...
249f454ebdeSmillert */
250f624271cSavsm if (snprintf(envstr, MAX_ENVSTR, "%s=%s", name, val) >= MAX_ENVSTR)
251a9e807deSmillert goto not_env;
252df930be7Sderaadt return (TRUE);
253a9e807deSmillert not_env:
254a9e807deSmillert fseek(f, filepos, SEEK_SET);
255a9e807deSmillert Set_LineNum(fileline);
256a9e807deSmillert return (FALSE);
257df930be7Sderaadt }
258