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