File: | src/usr.bin/vi/build/../ex/ex_init.c |
Warning: | line 224, column 19 The right operand of '!=' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ex_init.c,v 1.19 2021/10/24 21:24:17 deraadt Exp $ */ | |||
2 | ||||
3 | /*- | |||
4 | * Copyright (c) 1992, 1993, 1994 | |||
5 | * The Regents of the University of California. All rights reserved. | |||
6 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 | |||
7 | * Keith Bostic. All rights reserved. | |||
8 | * | |||
9 | * See the LICENSE file for redistribution information. | |||
10 | */ | |||
11 | ||||
12 | #include "config.h" | |||
13 | ||||
14 | #include <sys/queue.h> | |||
15 | #include <sys/stat.h> | |||
16 | ||||
17 | #include <bitstring.h> | |||
18 | #include <errno(*__errno()).h> | |||
19 | #include <fcntl.h> | |||
20 | #include <limits.h> | |||
21 | #include <stdio.h> | |||
22 | #include <stdlib.h> | |||
23 | #include <string.h> | |||
24 | #include <unistd.h> | |||
25 | ||||
26 | #include "../common/common.h" | |||
27 | #include "tag.h" | |||
28 | #include "pathnames.h" | |||
29 | ||||
30 | enum rc { NOEXIST, NOPERM, RCOK }; | |||
31 | static enum rc exrc_isok(SCR *, struct stat *, int *, char *, int, int); | |||
32 | ||||
33 | static int ex_run_file(SCR *, int, char *); | |||
34 | ||||
35 | /* | |||
36 | * ex_screen_copy -- | |||
37 | * Copy ex screen. | |||
38 | * | |||
39 | * PUBLIC: int ex_screen_copy(SCR *, SCR *); | |||
40 | */ | |||
41 | int | |||
42 | ex_screen_copy(SCR *orig, SCR *sp) | |||
43 | { | |||
44 | EX_PRIVATE *oexp, *nexp; | |||
45 | ||||
46 | /* Create the private ex structure. */ | |||
47 | CALLOC_RET(orig, nexp, 1, sizeof(EX_PRIVATE)){ if (((nexp) = calloc((1), (sizeof(EX_PRIVATE)))) == ((void * )0)) { msgq((orig), M_SYSERR, ((void *)0)); return (1); } }; | |||
48 | sp->ex_private = nexp; | |||
49 | ||||
50 | /* Initialize queues. */ | |||
51 | TAILQ_INIT(&nexp->tq)do { (&nexp->tq)->tqh_first = ((void *)0); (&nexp ->tq)->tqh_last = &(&nexp->tq)->tqh_first ; } while (0); | |||
52 | TAILQ_INIT(&nexp->tagfq)do { (&nexp->tagfq)->tqh_first = ((void *)0); (& nexp->tagfq)->tqh_last = &(&nexp->tagfq)-> tqh_first; } while (0); | |||
53 | ||||
54 | if (orig == NULL((void *)0)) { | |||
55 | } else { | |||
56 | oexp = EXP(orig)((EX_PRIVATE *)((orig)->ex_private)); | |||
57 | ||||
58 | if (oexp->lastbcomm != NULL((void *)0) && | |||
59 | (nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL((void *)0)) { | |||
60 | msgq(sp, M_SYSERR, NULL((void *)0)); | |||
61 | return(1); | |||
62 | } | |||
63 | if (ex_tag_copy(orig, sp)) | |||
64 | return (1); | |||
65 | } | |||
66 | return (0); | |||
67 | } | |||
68 | ||||
69 | /* | |||
70 | * ex_screen_end -- | |||
71 | * End a vi screen. | |||
72 | * | |||
73 | * PUBLIC: int ex_screen_end(SCR *); | |||
74 | */ | |||
75 | int | |||
76 | ex_screen_end(SCR *sp) | |||
77 | { | |||
78 | EX_PRIVATE *exp; | |||
79 | int rval; | |||
80 | ||||
81 | if ((exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private))) == NULL((void *)0)) | |||
82 | return (0); | |||
83 | ||||
84 | rval = 0; | |||
85 | ||||
86 | /* Close down script connections. */ | |||
87 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000))) && sscr_end(sp)) | |||
88 | rval = 1; | |||
89 | ||||
90 | if (argv_free(sp)) | |||
91 | rval = 1; | |||
92 | ||||
93 | free(exp->ibp); | |||
94 | free(exp->lastbcomm); | |||
95 | ||||
96 | if (ex_tag_free(sp)) | |||
97 | rval = 1; | |||
98 | ||||
99 | /* Free private memory. */ | |||
100 | free(exp); | |||
101 | sp->ex_private = NULL((void *)0); | |||
102 | ||||
103 | return (rval); | |||
104 | } | |||
105 | ||||
106 | /* | |||
107 | * ex_optchange -- | |||
108 | * Handle change of options for ex. | |||
109 | * | |||
110 | * PUBLIC: int ex_optchange(SCR *, int, char *, u_long *); | |||
111 | */ | |||
112 | int | |||
113 | ex_optchange(SCR *sp, int offset, char *str, u_long *valp) | |||
114 | { | |||
115 | switch (offset) { | |||
116 | case O_TAGS: | |||
117 | return (ex_tagf_alloc(sp, str)); | |||
118 | } | |||
119 | return (0); | |||
120 | } | |||
121 | ||||
122 | /* | |||
123 | * ex_exrc -- | |||
124 | * Read the EXINIT environment variable and the startup exrc files, | |||
125 | * and execute their commands. | |||
126 | * | |||
127 | * PUBLIC: int ex_exrc(SCR *); | |||
128 | */ | |||
129 | int | |||
130 | ex_exrc(SCR *sp) | |||
131 | { | |||
132 | struct stat hsb, lsb; | |||
133 | char *p, path[PATH_MAX1024]; | |||
134 | int fd; | |||
135 | ||||
136 | /* | |||
137 | * Source the system, environment, $HOME and local .exrc values. | |||
138 | * Vi historically didn't check $HOME/.exrc if the environment | |||
139 | * variable EXINIT was set. This is all done before the file is | |||
140 | * read in, because things in the .exrc information can set, for | |||
141 | * example, the recovery directory. | |||
142 | * | |||
143 | * !!! | |||
144 | * While nvi can handle any of the options settings of historic vi, | |||
145 | * the converse is not true. Since users are going to have to have | |||
146 | * files and environmental variables that work with both, we use nvi | |||
147 | * versions of both the $HOME and local startup files if they exist, | |||
148 | * otherwise the historic ones. | |||
149 | * | |||
150 | * !!! | |||
151 | * For a discussion of permissions and when what .exrc files are | |||
152 | * read, see the comment above the exrc_isok() function below. | |||
153 | * | |||
154 | * !!! | |||
155 | * If the user started the historic of vi in $HOME, vi read the user's | |||
156 | * .exrc file twice, as $HOME/.exrc and as ./.exrc. We avoid this, as | |||
157 | * it's going to make some commands behave oddly, and I can't imagine | |||
158 | * anyone depending on it. | |||
159 | */ | |||
160 | switch (exrc_isok(sp, &hsb, &fd, _PATH_SYSEXRC"/etc/vi.exrc", 1, 0)) { | |||
| ||||
161 | case NOEXIST: | |||
162 | case NOPERM: | |||
163 | break; | |||
164 | case RCOK: | |||
165 | if (ex_run_file(sp, fd, _PATH_SYSEXRC"/etc/vi.exrc")) | |||
166 | return (1); | |||
167 | break; | |||
168 | } | |||
169 | ||||
170 | /* Run the commands. */ | |||
171 | if (EXCMD_RUNNING(sp->gp)(((&(sp->gp)->ecq)->lh_first)->clen != 0)) | |||
172 | (void)ex_cmd(sp); | |||
173 | if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)(((sp)->flags) & ((0x00000200 | 0x00000400)))) | |||
174 | return (0); | |||
175 | ||||
176 | if ((p = getenv("NEXINIT")) != NULL((void *)0)) { | |||
177 | if (ex_run_str(sp, "NEXINIT", p, strlen(p), 1, 0)) | |||
178 | return (1); | |||
179 | } else if ((p = getenv("EXINIT")) != NULL((void *)0)) { | |||
180 | if (ex_run_str(sp, "EXINIT", p, strlen(p), 1, 0)) | |||
181 | return (1); | |||
182 | } else if ((p = getenv("HOME")) != NULL((void *)0) && *p) { | |||
183 | (void)snprintf(path, sizeof(path), "%s/%s", p, _PATH_NEXRC".nexrc"); | |||
184 | switch (exrc_isok(sp, &hsb, &fd, path, 0, 1)) { | |||
185 | case NOEXIST: | |||
186 | (void)snprintf(path, | |||
187 | sizeof(path), "%s/%s", p, _PATH_EXRC".exrc"); | |||
188 | if (exrc_isok(sp, &hsb, &fd, path, 0, 1) == RCOK && | |||
189 | ex_run_file(sp, fd, path)) | |||
190 | return (1); | |||
191 | break; | |||
192 | case NOPERM: | |||
193 | break; | |||
194 | case RCOK: | |||
195 | if (ex_run_file(sp, fd, path)) | |||
196 | return (1); | |||
197 | break; | |||
198 | } | |||
199 | } | |||
200 | ||||
201 | /* Run the commands. */ | |||
202 | if (EXCMD_RUNNING(sp->gp)(((&(sp->gp)->ecq)->lh_first)->clen != 0)) | |||
203 | (void)ex_cmd(sp); | |||
204 | if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)(((sp)->flags) & ((0x00000200 | 0x00000400)))) | |||
205 | return (0); | |||
206 | ||||
207 | /* Previous commands may have set the exrc option. */ | |||
208 | if (O_ISSET(sp, O_EXRC)((((&(((sp)))->opts[(((O_EXRC)))])->flags) & (( 0x01))) ? (((sp)))->gp->opts[(((sp)))->opts[(((O_EXRC )))].o_cur.val].o_cur.val : (((sp)))->opts[(((O_EXRC)))].o_cur .val)) { | |||
209 | switch (exrc_isok(sp, &lsb, &fd, _PATH_NEXRC".nexrc", 0, 0)) { | |||
210 | case NOEXIST: | |||
211 | if (exrc_isok(sp, &lsb, &fd, _PATH_EXRC".exrc", 0, 0) | |||
212 | == RCOK) { | |||
213 | if (lsb.st_dev != hsb.st_dev || | |||
214 | lsb.st_ino != hsb.st_ino) { | |||
215 | if (ex_run_file(sp, fd, _PATH_EXRC".exrc")) | |||
216 | return (1); | |||
217 | } else | |||
218 | close(fd); | |||
219 | } | |||
220 | break; | |||
221 | case NOPERM: | |||
222 | break; | |||
223 | case RCOK: | |||
224 | if (lsb.st_dev != hsb.st_dev || | |||
| ||||
225 | lsb.st_ino != hsb.st_ino) { | |||
226 | if (ex_run_file(sp, fd, _PATH_NEXRC".nexrc")) | |||
227 | return (1); | |||
228 | } else | |||
229 | close(fd); | |||
230 | break; | |||
231 | } | |||
232 | /* Run the commands. */ | |||
233 | if (EXCMD_RUNNING(sp->gp)(((&(sp->gp)->ecq)->lh_first)->clen != 0)) | |||
234 | (void)ex_cmd(sp); | |||
235 | if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)(((sp)->flags) & ((0x00000200 | 0x00000400)))) | |||
236 | return (0); | |||
237 | } | |||
238 | ||||
239 | return (0); | |||
240 | } | |||
241 | ||||
242 | /* | |||
243 | * ex_run_file -- | |||
244 | * Set up a file of ex commands to run. | |||
245 | */ | |||
246 | static int | |||
247 | ex_run_file(SCR *sp, int fd, char *name) | |||
248 | { | |||
249 | ARGS *ap[2], a; | |||
250 | EXCMD cmd; | |||
251 | ||||
252 | ex_cinit(&cmd, C_SOURCE, 0, OOBLNO0, OOBLNO0, 0, ap); | |||
253 | ex_cadd(&cmd, &a, name, strlen(name)); | |||
254 | return (ex_sourcefd(sp, &cmd, fd)); | |||
255 | } | |||
256 | ||||
257 | /* | |||
258 | * ex_run_str -- | |||
259 | * Set up a string of ex commands to run. | |||
260 | * | |||
261 | * PUBLIC: int ex_run_str(SCR *, char *, char *, size_t, int, int); | |||
262 | */ | |||
263 | int | |||
264 | ex_run_str(SCR *sp, char *name, char *str, size_t len, int ex_flags, | |||
265 | int nocopy) | |||
266 | { | |||
267 | GS *gp; | |||
268 | EXCMD *ecp; | |||
269 | ||||
270 | gp = sp->gp; | |||
271 | if (EXCMD_RUNNING(gp)(((&(gp)->ecq)->lh_first)->clen != 0)) { | |||
272 | CALLOC_RET(sp, ecp, 1, sizeof(EXCMD)){ if (((ecp) = calloc((1), (sizeof(EXCMD)))) == ((void *)0)) { msgq((sp), M_SYSERR, ((void *)0)); return (1); } }; | |||
273 | LIST_INSERT_HEAD(&gp->ecq, ecp, q)do { if (((ecp)->q.le_next = (&gp->ecq)->lh_first ) != ((void *)0)) (&gp->ecq)->lh_first->q.le_prev = &(ecp)->q.le_next; (&gp->ecq)->lh_first = (ecp); (ecp)->q.le_prev = &(&gp->ecq)->lh_first ; } while (0); | |||
274 | } else | |||
275 | ecp = &gp->excmd; | |||
276 | ||||
277 | F_INIT(ecp,((ecp)->flags) = ((ex_flags ? 0x00000800 | 0x00002000 | 0x00004000 | 0x00020000 : 0)) | |||
278 | ex_flags ? E_BLIGNORE | E_NOAUTO | E_NOPRDEF | E_VLITONLY : 0)((ecp)->flags) = ((ex_flags ? 0x00000800 | 0x00002000 | 0x00004000 | 0x00020000 : 0)); | |||
279 | ||||
280 | if (nocopy) | |||
281 | ecp->cp = str; | |||
282 | else | |||
283 | if ((ecp->cp = v_strdup(sp, str, len)) == NULL((void *)0)) | |||
284 | return (1); | |||
285 | ecp->clen = len; | |||
286 | ||||
287 | if (name == NULL((void *)0)) | |||
288 | ecp->if_name = NULL((void *)0); | |||
289 | else { | |||
290 | if ((ecp->if_name = v_strdup(sp, name, strlen(name))) == NULL((void *)0)) | |||
291 | return (1); | |||
292 | ecp->if_lno = 1; | |||
293 | F_SET(ecp, E_NAMEDISCARD)(((ecp)->flags) |= ((0x00001000))); | |||
294 | } | |||
295 | ||||
296 | return (0); | |||
297 | } | |||
298 | ||||
299 | /* | |||
300 | * exrc_isok -- | |||
301 | * Open and check a .exrc file for source-ability. | |||
302 | * | |||
303 | * !!! | |||
304 | * Historically, vi read the $HOME and local .exrc files if they were owned | |||
305 | * by the user's real ID, or the "sourceany" option was set, regardless of | |||
306 | * any other considerations. We no longer support the sourceany option as | |||
307 | * it's a security problem of mammoth proportions. We require the system | |||
308 | * .exrc file to be owned by root, the $HOME .exrc file to be owned by the | |||
309 | * user's effective ID (or that the user's effective ID be root) and the | |||
310 | * local .exrc files to be owned by the user's effective ID. In all cases, | |||
311 | * the file cannot be writeable by anyone other than its owner. | |||
312 | * | |||
313 | * In O'Reilly ("Learning the VI Editor", Fifth Ed., May 1992, page 106), | |||
314 | * it notes that System V release 3.2 and later has an option "[no]exrc". | |||
315 | * The behavior is that local .exrc files are read only if the exrc option | |||
316 | * is set. The default for the exrc option was off, so, by default, local | |||
317 | * .exrc files were not read. The problem this was intended to solve was | |||
318 | * that System V permitted users to give away files, so there's no possible | |||
319 | * ownership or writeability test to ensure that the file is safe. | |||
320 | * | |||
321 | * POSIX 1003.2-1992 standardized exrc as an option. It required the exrc | |||
322 | * option to be off by default, thus local .exrc files are not to be read | |||
323 | * by default. The Rationale noted (incorrectly) that this was a change | |||
324 | * to historic practice, but correctly noted that a default of off improves | |||
325 | * system security. POSIX also required that vi check the effective user | |||
326 | * ID instead of the real user ID, which is why we've switched from historic | |||
327 | * practice. | |||
328 | * | |||
329 | * We initialize the exrc variable to off. If it's turned on by the system | |||
330 | * or $HOME .exrc files, and the local .exrc file passes the ownership and | |||
331 | * writeability tests, then we read it. This breaks historic 4BSD practice, | |||
332 | * but it gives us a measure of security on systems where users can give away | |||
333 | * files. | |||
334 | */ | |||
335 | static enum rc | |||
336 | exrc_isok(SCR *sp, struct stat *sbp, int *fdp, char *path, int rootown, | |||
337 | int rootid) | |||
338 | { | |||
339 | enum { ROOTOWN, OWN, WRITER } etype; | |||
340 | uid_t euid; | |||
341 | int nf1, nf2; | |||
342 | char *a, *b, buf[PATH_MAX1024]; | |||
343 | ||||
344 | if ((*fdp = open(path, O_RDONLY0x0000)) < 0) { | |||
345 | if (errno(*__errno()) == ENOENT2) | |||
346 | /* This is the only case where ex_exrc() | |||
347 | * should silently try the next file, for | |||
348 | * example .exrc after .nexrc. | |||
349 | */ | |||
350 | return (NOEXIST); | |||
351 | ||||
352 | msgq_str(sp, M_SYSERR, path, "%s"); | |||
353 | return (NOPERM); | |||
354 | } | |||
355 | ||||
356 | if (fstat(*fdp, sbp)) { | |||
357 | msgq_str(sp, M_SYSERR, path, "%s"); | |||
358 | close(*fdp); | |||
359 | return (NOPERM); | |||
360 | } | |||
361 | ||||
362 | /* Check ownership permissions. */ | |||
363 | euid = geteuid(); | |||
364 | if (!(rootown && sbp->st_uid == 0) && | |||
365 | !(rootid && euid == 0) && sbp->st_uid != euid) { | |||
366 | etype = rootown ? ROOTOWN : OWN; | |||
367 | goto denied; | |||
368 | } | |||
369 | ||||
370 | /* Check writeability. */ | |||
371 | if (sbp->st_mode & (S_IWGRP0000020 | S_IWOTH0000002)) { | |||
372 | etype = WRITER; | |||
373 | goto denied; | |||
374 | } | |||
375 | return (RCOK); | |||
376 | ||||
377 | denied: a = msg_print(sp, path, &nf1); | |||
378 | if (strchr(path, '/') == NULL((void *)0) && getcwd(buf, sizeof(buf)) != NULL((void *)0)) { | |||
379 | b = msg_print(sp, buf, &nf2); | |||
380 | switch (etype) { | |||
381 | case ROOTOWN: | |||
382 | msgq(sp, M_ERR, | |||
383 | "%s/%s: not sourced: not owned by you or root", | |||
384 | b, a); | |||
385 | break; | |||
386 | case OWN: | |||
387 | msgq(sp, M_ERR, | |||
388 | "%s/%s: not sourced: not owned by you", b, a); | |||
389 | break; | |||
390 | case WRITER: | |||
391 | msgq(sp, M_ERR, | |||
392 | "%s/%s: not sourced: writable by a user other than the owner", b, a); | |||
393 | break; | |||
394 | } | |||
395 | if (nf2) | |||
396 | FREE_SPACE(sp, b, 0){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (b) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(b); }; | |||
397 | } else | |||
398 | switch (etype) { | |||
399 | case ROOTOWN: | |||
400 | msgq(sp, M_ERR, | |||
401 | "%s: not sourced: not owned by you or root", a); | |||
402 | break; | |||
403 | case OWN: | |||
404 | msgq(sp, M_ERR, | |||
405 | "%s: not sourced: not owned by you", a); | |||
406 | break; | |||
407 | case WRITER: | |||
408 | msgq(sp, M_ERR, | |||
409 | "%s: not sourced: writable by a user other than the owner", a); | |||
410 | break; | |||
411 | } | |||
412 | ||||
413 | if (nf1) | |||
414 | FREE_SPACE(sp, a, 0){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (a) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(a); }; | |||
415 | close(*fdp); | |||
416 | return (NOPERM); | |||
417 | } |