| 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 | } |