1 /* $OpenBSD: env.c,v 1.34 2022/05/21 01:21:29 deraadt Exp $ */
2
3 /* Copyright 1988,1990,1993,1994 by Paul Vixie
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/types.h>
21
22 #include <bitstring.h> /* for structs.h */
23 #include <ctype.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h> /* for structs.h */
29
30 #include "macros.h"
31 #include "structs.h"
32 #include "funcs.h"
33 #include "globals.h"
34
35 char **
env_init(void)36 env_init(void)
37 {
38 char **p = malloc(sizeof(char *));
39
40 if (p != NULL)
41 p[0] = NULL;
42 return (p);
43 }
44
45 void
env_free(char ** envp)46 env_free(char **envp)
47 {
48 char **p;
49
50 for (p = envp; *p != NULL; p++)
51 free(*p);
52 free(envp);
53 }
54
55 char **
env_copy(char ** envp)56 env_copy(char **envp)
57 {
58 int count, i, save_errno;
59 char **p;
60
61 for (count = 0; envp[count] != NULL; count++)
62 continue;
63 p = reallocarray(NULL, count+1, sizeof(char *)); /* 1 for the NULL */
64 if (p != NULL) {
65 for (i = 0; i < count; i++)
66 if ((p[i] = strdup(envp[i])) == NULL) {
67 save_errno = errno;
68 while (--i >= 0)
69 free(p[i]);
70 free(p);
71 errno = save_errno;
72 return (NULL);
73 }
74 p[count] = NULL;
75 }
76 return (p);
77 }
78
79 static char *
env_find(char * name,char ** envp,size_t * count)80 env_find(char *name, char **envp, size_t *count)
81 {
82 char **ep, *p, *q;
83 size_t len;
84
85 /*
86 * Find name in envp and return its value along with the
87 * index it was found at or the length of envp if not found.
88 * We treat a '=' in name as end of string for env_set().
89 */
90 for (p = name; *p && *p != '='; p++)
91 continue;
92 len = (size_t)(p - name);
93 for (ep = envp; (p = *ep) != NULL; ep++) {
94 if ((q = strchr(p, '=')) == NULL)
95 continue;
96 if ((size_t)(q - p) == len && strncmp(p, name, len) == 0) {
97 p = q + 1;
98 break;
99 }
100 }
101 *count = (size_t)(ep - envp);
102 return (p);
103 }
104
105 char *
env_get(char * name,char ** envp)106 env_get(char *name, char **envp)
107 {
108 size_t count;
109
110 return (env_find(name, envp, &count));
111 }
112
113 char **
env_set(char ** envp,char * envstr)114 env_set(char **envp, char *envstr)
115 {
116 size_t count;
117 char **p, *envcopy;
118
119 if ((envcopy = strdup(envstr)) == NULL)
120 return (NULL);
121
122 /* Replace existing name if found. */
123 if (env_find(envstr, envp, &count) != NULL) {
124 free(envp[count]);
125 envp[count] = envcopy;
126 return (envp);
127 }
128
129 /* Realloc envp and append new variable. */
130 p = reallocarray(envp, count + 2, sizeof(char **));
131 if (p == NULL) {
132 free(envcopy);
133 return (NULL);
134 }
135 p[count++] = envcopy;
136 p[count] = NULL;
137 return (p);
138 }
139
140 /* The following states are used by load_env(), traversed in order: */
141 enum env_state {
142 NAMEI, /* First char of NAME, may be quote */
143 NAME, /* Subsequent chars of NAME */
144 EQ1, /* After end of name, looking for '=' sign */
145 EQ2, /* After '=', skipping whitespace */
146 VALUEI, /* First char of VALUE, may be quote */
147 VALUE, /* Subsequent chars of VALUE */
148 FINI, /* All done, skipping trailing whitespace */
149 ERROR /* Error */
150 };
151
152 /* return -1 = end of file
153 * FALSE = not an env setting (file was repositioned)
154 * TRUE = was an env setting
155 */
156 int
load_env(char * envstr,FILE * f)157 load_env(char *envstr, FILE *f)
158 {
159 long filepos;
160 int fileline;
161 enum env_state state;
162 char name[MAX_ENVSTR], val[MAX_ENVSTR];
163 char quotechar, *c, *str;
164
165 filepos = ftell(f);
166 fileline = LineNumber;
167 skip_comments(f);
168 if (get_string(envstr, MAX_ENVSTR, f, "\n") == EOF)
169 return (-1);
170
171 bzero(name, sizeof name);
172 bzero(val, sizeof val);
173 str = name;
174 state = NAMEI;
175 quotechar = '\0';
176 c = envstr;
177 while (state != ERROR && *c) {
178 switch (state) {
179 case NAMEI:
180 case VALUEI:
181 if (*c == '\'' || *c == '"')
182 quotechar = *c++;
183 state++;
184 /* FALLTHROUGH */
185 case NAME:
186 case VALUE:
187 if (quotechar) {
188 if (*c == quotechar) {
189 state++;
190 c++;
191 break;
192 }
193 if (state == NAME && *c == '=') {
194 state = ERROR;
195 break;
196 }
197 } else {
198 if (state == NAME) {
199 if (isspace((unsigned char)*c)) {
200 c++;
201 state++;
202 break;
203 }
204 if (*c == '=') {
205 state++;
206 break;
207 }
208 }
209 }
210 *str++ = *c++;
211 break;
212 case EQ1:
213 if (*c == '=') {
214 state++;
215 str = val;
216 quotechar = '\0';
217 } else {
218 if (!isspace((unsigned char)*c))
219 state = ERROR;
220 }
221 c++;
222 break;
223 case EQ2:
224 case FINI:
225 if (isspace((unsigned char)*c))
226 c++;
227 else
228 state++;
229 break;
230 case ERROR:
231 /* handled below */
232 break;
233 }
234 }
235 if (state != FINI && !(state == VALUE && !quotechar))
236 goto not_env;
237 if (state == VALUE) {
238 /* End of unquoted value: trim trailing whitespace */
239 c = val + strlen(val);
240 while (c > val && isspace((unsigned char)c[-1]))
241 *(--c) = '\0';
242 }
243
244 /* 2 fields from parser; looks like an env setting */
245
246 /*
247 * This can't overflow because get_string() limited the size of the
248 * name and val fields. Still, it doesn't hurt to be careful...
249 */
250 if (snprintf(envstr, MAX_ENVSTR, "%s=%s", name, val) >= MAX_ENVSTR)
251 goto not_env;
252 return (TRUE);
253 not_env:
254 fseek(f, filepos, SEEK_SET);
255 Set_LineNum(fileline);
256 return (FALSE);
257 }
258