├── .git-author-conv-file ├── .gitignore ├── GNUmakefile ├── LICENSE ├── README.md ├── configure ├── doas.1 ├── doas.c ├── doas.conf.5 ├── doas.h ├── env.c ├── libopenbsd ├── bsd-setres_id.c ├── closefrom.c ├── errc.c ├── execvpe.c ├── explicit_bzero.c ├── openbsd.h ├── progname.c ├── readpassphrase.c ├── reallocarray.c ├── strlcat.c ├── strlcpy.c ├── strtonum.c ├── sys-readpassphrase.h ├── sys-time.h ├── sys-tree.h └── verrc.c ├── pam.c ├── parse.y ├── shadow.c └── timestamp.c /.git-author-conv-file: -------------------------------------------------------------------------------- 1 | bcallah=Brian Callahan 2 | benno= 3 | bentley= 4 | deraadt=Theo de Raadt 5 | doug= 6 | espie=Marc Espie 7 | jmc=Jean-Marie Cannie 8 | nicm=Nicholas Marriott 9 | schwarze=Ingo Schwarze 10 | tedu=Ted Unangst 11 | zhuk=Vadim Zhukov 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doas 2 | 3 | parse.c 4 | version.h 5 | 6 | *.a 7 | *.d 8 | *.o 9 | 10 | *.swp 11 | *.swo 12 | 13 | config.mk 14 | config.h 15 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | PROG= doas 2 | MAN= doas.1 doas.conf.5 3 | 4 | SRCS= parse.y doas.c env.c 5 | 6 | include config.mk 7 | 8 | override CFLAGS:=-I. -Ilibopenbsd -O2 -Wall -Wextra ${OS_CFLAGS} ${CFLAGS} 9 | 10 | all: ${PROG} 11 | 12 | OBJS:= ${SRCS:.y=.c} 13 | OBJS:= ${OBJS:.c=.o} 14 | 15 | ${PROG}: ${OBJS} 16 | ${CC} ${CFLAGS} $^ -o $@ ${LDFLAGS} ${LDLIBS} 17 | 18 | install: ${PROG} ${MAN} 19 | mkdir -p -m 0755 ${DESTDIR}${BINDIR} 20 | mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man1 21 | mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man5 22 | cp -f ${PROG} ${DESTDIR}${BINDIR} 23 | chown ${BINOWN}:${BINGRP} ${DESTDIR}${BINDIR}/${PROG} 24 | chmod ${BINMODE} ${DESTDIR}${BINDIR}/${PROG} 25 | cp -f doas.1 ${DESTDIR}${MANDIR}/man1 26 | cp -f doas.conf.5 ${DESTDIR}${MANDIR}/man5 27 | 28 | uninstall: 29 | rm -f ${DESTDIR}${BINDIR}/${PROG} 30 | rm -f ${DESTDIR}${PAMDIR}/doas 31 | rm -f ${DESTDIR}${MANDIR}/man1/doas.1 32 | rm -f ${DESTDIR}${MANDIR}/man5/doas.conf.5 33 | 34 | clean: 35 | rm -f ${PROG} ${OBJS} ${OBJS:.o=.d} parse.c 36 | 37 | -include ${OBJS:.o=.d} 38 | 39 | .PHONY: all clean install uninstall 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ted Unangst 2 | Copyright (c) 2015 Nathan Holstein 3 | Copyright (c) 2016 Duncan Overbruck 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | 18 | Additional bits copyright of their respective authors, see individual 19 | files for details. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenDoas: a portable version of OpenBSD's `doas` command 2 | 3 | [`doas`](https://en.wikipedia.org/wiki/Doas) is a minimal replacement for the venerable `sudo`. It was 4 | initially [written by Ted Unangst](http://www.tedunangst.com/flak/post/doas) 5 | of the OpenBSD project to provide 95% of the features of `sudo` with a 6 | fraction of the codebase. 7 | 8 | ## Building and Installation Warnings 9 | 10 | There are a few steps you have to carefully consider before building and installing 11 | OpenDoas: 12 | 13 | * There are fewer eyes on random `doas` ports, just because `sudo` had a vulnerability 14 | does not mean random doas ports are more secure if they are not reviewed 15 | or [PAM](https://en.wikipedia.org/wiki/Pluggable_authentication_module) is configured incorrectly. 16 | * If you want to use PAM; You have to [configure PAM](#pam-configuration) 17 | and failing to do so correctly might leave a big open door. 18 | 19 | * Use the `configure` script. 20 | * Use the default make target. 21 | * If you really want to install a setuid binary that depends on 22 | PAM being correctly configured, use the `make install` target 23 | to install the software. 24 | 25 | ## About the OpenDoas Port 26 | 27 | This is not an official port/project from OpenBSD! 28 | 29 | As much as possible I've attempted to stick to `doas` as tedu desired 30 | it. As things stand it's essentially just code lifted from OpenBSD with 31 | PAM or shadow based authentication glommed on to it. 32 | 33 | Compatibility functions in libopenbsd come from OpenBSD directly 34 | (`strtonum.c`, `reallocarray.c`, `strlcpy.c`, `strlcat.c`), 35 | from openssh (`readpassphrase.c`) or from sudo (`closefrom.c`). 36 | 37 | The PAM and shadow authentication code does not come from the OpenBSD project. 38 | 39 | ### PAM Configuration 40 | 41 | I will not ship PAM configuration files, they are distribution specific and 42 | its simply not safe or productive to ship and install those files. 43 | 44 | If you want to use OpenDoas on your system and there is no package that 45 | ships with a working PAM configuration file, then you have to write and 46 | test it yourself. 47 | 48 | A good starting point is probably the distribution maintained `/etc/pam.d/sudo` 49 | file. 50 | 51 | ### Persist/Timestamp/Timeout 52 | 53 | The persist feature is disabled by default and can be enabled with the 54 | `--with-timestamp` configure flag. 55 | 56 | This feature is new and potentially dangerous, in the original `doas`, a kernel API 57 | is used to set and clear timeouts. This API is OpenBSD specific and no similar API 58 | is available on other operating systems. 59 | 60 | As a workaround, the persist feature is implemented using timestamp files 61 | similar to `sudo`. 62 | 63 | See the comment block in `timestamp.c` for an in-depth description on how 64 | timestamps are created and checked to be as safe as possible. 65 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | die() { 4 | printf "$1\n" >&2 5 | exit 1 6 | } 7 | 8 | usage() { 9 | cat <$CONFIG_H 77 | #ifndef CONFIG_H 78 | #define CONFIG_H 79 | 80 | ! 81 | 82 | if [ -z "$BUILD" ]; then 83 | BUILD="$(uname -m)-unknown-$(uname -s | tr '[:upper:]' '[:lower:]')" 84 | fi 85 | if [ -z "$HOST" ]; then 86 | [ -z "$TARGET" ] && TARGET=$BUILD 87 | HOST=$TARGET 88 | fi 89 | if [ -z "$TARGET" ]; then 90 | [ -z "$HOST" ] && HOST=$BUILD 91 | TARGET=$HOST 92 | fi 93 | 94 | if [ -z "$OS" ]; then 95 | # Derive OS from cpu-manufacturer-os-kernel 96 | CPU=${TARGET%%-*} 97 | REST=${TARGET#*-} 98 | MANU=${REST%%-*} 99 | REST=${REST#*-} 100 | OS=${REST%%-*} 101 | REST=${REST#*-} 102 | KERNEL=${REST%%-*} 103 | fi 104 | 105 | OS_CFLAGS="-D__${OS}__" 106 | 107 | case "$OS" in 108 | linux) 109 | printf 'Setting UID_MAX\t\t\t\t%d.\n' "$UID_MAX" >&2 110 | printf '#define UID_MAX %s\n' "$UID_MAX" >>$CONFIG_H 111 | printf 'Setting GID_MAX\t\t\t\t%d.\n' "$GID_MAX" >&2 112 | printf '#define GID_MAX %s\n' "$GID_MAX" >>$CONFIG_H 113 | OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE" 114 | ;; 115 | netbsd) 116 | OS_CFLAGS="$OS_CFLAGS -D_OPENBSD_SOURCE" 117 | printf 'LDLIBS += -lutil\n' >>$CONFIG_MK 118 | : ${BINGRP:=wheel} 119 | ;; 120 | freebsd) 121 | printf 'LDLIBS += -lutil\n' >>$CONFIG_MK 122 | : ${BINGRP:=wheel} 123 | ;; 124 | darwin) 125 | : ${BINGRP:=wheel} 126 | ;; 127 | esac 128 | 129 | : ${PREFIX:=/usr/local} 130 | : ${EPREFIX:=${PREFIX}} 131 | : ${BINDIR:=${PREFIX}/bin} 132 | : ${SHAREDIR:=${PREFIX}/share} 133 | : ${MANDIR:=${SHAREDIR}/man} 134 | : ${SYSCONFDIR:=/etc} 135 | : ${BINMODE:=4755} 136 | : ${BINOWN:=root} 137 | : ${BINGRP:=root} 138 | 139 | cat <>$CONFIG_MK 140 | PREFIX ?= ${PREFIX} 141 | EPREFIX ?= ${EPREFIX} 142 | BINDIR ?= ${BINDIR} 143 | SHAREDIR ?= ${SHAREDIR} 144 | MANDIR ?= ${MANDIR} 145 | SYSCONFDIR?= ${SYSCONFDIR} 146 | BINMODE ?= ${BINMODE} 147 | BINOWN ?= ${BINOWN} 148 | BINGRP ?= ${BINGRP} 149 | EOF 150 | 151 | [ -n "$OS_CFLAGS" ] && \ 152 | printf 'OS_CFLAGS += %s\n' "$OS_CFLAGS" >>$CONFIG_MK 153 | 154 | [ -n "$DEBUG" ] && \ 155 | printf 'CFLAGS += -O0 -g\n' >>$CONFIG_MK 156 | 157 | [ -n "$BUILD_STATIC" ] && \ 158 | printf 'CFLAGS += -static\n' >>$CONFIG_MK 159 | 160 | # Add CPPFLAGS/CFLAGS/LDFLAGS/LDLIBS to CC for testing features 161 | XCC="${CC:=cc} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS $LDLIBS" 162 | # Make sure to disable --as-needed for CC tests. 163 | 164 | case "$OS" in 165 | darwin) ;; 166 | *) XCC="$XCC -Wl,--no-as-needed" ;; 167 | esac 168 | 169 | check_func() { 170 | func="$1"; src="$2"; shift 2 171 | printf 'Checking for %-14s\t\t' "$func ..." >&2 172 | printf '%s\n' "$src" >"_$func.c" 173 | $XCC "_$func.c" -o "_$func" 2>/dev/null 174 | ret=$? 175 | rm -f "_$func.c" "_$func" 176 | upperfunc="$(printf '%s\n' "$func" | tr '[[:lower:]]' '[[:upper:]]')" 177 | if [ $ret -eq 0 ]; then 178 | printf 'yes.\n' >&2 179 | printf '#define HAVE_%s\n' "$upperfunc" >>$CONFIG_H 180 | return 0 181 | else 182 | printf '/* #define HAVE_%s */\n' "$upperfunc" >>$CONFIG_H 183 | printf 'no.\n' >&2 184 | return 1 185 | fi 186 | } 187 | 188 | authmethod() { 189 | # 190 | # Check for pam_appl.h. 191 | # 192 | src=' 193 | #include 194 | int main(void) { 195 | return 0; 196 | }' 197 | [ -z "$WITHOUT_PAM" ] && check_func "pam_appl_h" "$src" && { 198 | printf 'SRCS += pam.c\n' >>$CONFIG_MK 199 | printf 'LDLIBS += -lpam\n' >>$CONFIG_MK 200 | printf '#define USE_PAM\n' >>$CONFIG_H 201 | printf 'pam\n' 202 | return 0 203 | } 204 | 205 | # 206 | # Check for shadow.h. 207 | # 208 | src=' 209 | #include 210 | int main(void) { 211 | return 0; 212 | }' 213 | [ -z "$WITHOUT_SHADOW" ] && check_func "shadow_h" "$src" && { 214 | printf 'SRCS += shadow.c\n' >>$CONFIG_MK 215 | printf 'LDLIBS += -lcrypt\n' >>$CONFIG_MK 216 | printf '#define USE_SHADOW\n' >>$CONFIG_H 217 | printf 'shadow\n' 218 | return 0 219 | } 220 | 221 | return 1 222 | } 223 | 224 | persistmethod() { 225 | [ -z "$WITHOUT_TIMESTAMP" ] && { 226 | printf '#define USE_TIMESTAMP\n' >>$CONFIG_H 227 | printf 'SRCS += timestamp.c\n' >>$CONFIG_MK 228 | printf 'timestamp\n' 229 | return 0 230 | } 231 | return 1 232 | } 233 | 234 | # 235 | # Check for explicit_bzero(). 236 | # 237 | src=' 238 | #include 239 | int main(void) { 240 | explicit_bzero(NULL, 0); 241 | return 0; 242 | }' 243 | check_func "explicit_bzero" "$src" || { 244 | printf 'SRCS += libopenbsd/explicit_bzero.c\n' >>$CONFIG_MK 245 | } 246 | 247 | # 248 | # Check for strlcat(). 249 | # 250 | src=' 251 | #include 252 | int main(void) { 253 | const char s1[] = "foo"; 254 | char s2[10]; 255 | strlcat(s2, s1, sizeof(s2)); 256 | return 0; 257 | }' 258 | check_func "strlcat" "$src" || { 259 | printf 'SRCS += libopenbsd/strlcat.c\n' >>$CONFIG_MK 260 | } 261 | 262 | # 263 | # Check for strlcpy(). 264 | # 265 | src=' 266 | #include 267 | int main(void) { 268 | const char s1[] = "foo"; 269 | char s2[10]; 270 | strlcpy(s2, s1, sizeof(s2)); 271 | return 0; 272 | }' 273 | check_func "strlcpy" "$src" || { 274 | printf 'SRCS += libopenbsd/strlcpy.c\n' >>$CONFIG_MK 275 | } 276 | 277 | # 278 | # Check for errc(). 279 | # 280 | src=' 281 | #include 282 | int main(void) { 283 | errc(0, 0, ""); 284 | return 0; 285 | }' 286 | check_func "errc" "$src" || { 287 | printf 'SRCS += libopenbsd/errc.c\n' >>$CONFIG_MK 288 | } 289 | 290 | # 291 | # Check for verrc(). 292 | # 293 | src=' 294 | #include 295 | #include 296 | int main(void) { 297 | verrc(0, 0, "x", NULL); 298 | return 0; 299 | }' 300 | check_func "verrc" "$src" || { 301 | printf 'SRCS += libopenbsd/verrc.c\n' >>$CONFIG_MK 302 | } 303 | 304 | # 305 | # Check for setprogname(). 306 | # 307 | src=' 308 | #include 309 | int main(void) { 310 | setprogname(""); 311 | return 0; 312 | }' 313 | check_func "setprogname" "$src" || { 314 | printf 'SRCS += libopenbsd/progname.c\n' >>$CONFIG_MK 315 | } 316 | 317 | # 318 | # Check for readpassphrase(). 319 | # 320 | src=' 321 | #include 322 | int main(void) { 323 | char buf[12]; 324 | readpassphrase("", buf, sizeof(buf), 0); 325 | return 0; 326 | }' 327 | check_func "readpassphrase" "$src" || { 328 | printf 'SRCS += libopenbsd/readpassphrase.c\n' >>$CONFIG_MK 329 | } 330 | 331 | # 332 | # Check for strtonum(). 333 | # 334 | src=' 335 | #include 336 | int main(void) { 337 | const char *errstr; 338 | strtonum("", 1, 64, &errstr); 339 | return 0; 340 | }' 341 | check_func "strtonum" "$src" || { 342 | printf 'SRCS += libopenbsd/strtonum.c\n' >>$CONFIG_MK 343 | } 344 | 345 | # 346 | # Check for reallocarray(). 347 | # 348 | src=' 349 | #include 350 | int main(void) { 351 | reallocarray(NULL, 0, 0); 352 | return 0; 353 | }' 354 | check_func "reallocarray" "$src" || { 355 | printf 'SRCS += libopenbsd/reallocarray.c\n' >>$CONFIG_MK 356 | } 357 | 358 | # 359 | # Check for execvpe(). 360 | # 361 | src=' 362 | #include 363 | int main(void) { 364 | const char *p = { "", NULL }; 365 | execvpe("", p, p); 366 | return 0; 367 | }' 368 | check_func "execvpe" "$src" || { 369 | printf 'SRCS += libopenbsd/execvpe.c\n' >>$CONFIG_MK 370 | } 371 | 372 | # 373 | # Check for setresuid(). 374 | # 375 | src=' 376 | #include 377 | int main(void) { 378 | setresuid(0, 0, 0); 379 | return 0; 380 | }' 381 | check_func "setresuid" "$src" 382 | have_setresuid=$? 383 | 384 | # 385 | # Check for setresgid(). 386 | # 387 | src=' 388 | #include 389 | int main(void) { 390 | setresgid(0, 0, 0); 391 | return 0; 392 | }' 393 | check_func "setresgid" "$src" 394 | have_setresgid=$? 395 | 396 | if [ $have_setresuid -eq 1 -o $have_setresgid -eq 1 ]; then 397 | printf 'SRCS += libopenbsd/bsd-setres_id.c\n' >>$CONFIG_MK 398 | fi 399 | 400 | # 401 | # Check for setreuid(). 402 | # 403 | src=' 404 | #include 405 | int main(void) { 406 | setreuid(0, 0); 407 | return 0; 408 | }' 409 | check_func "setreuid" "$src" 410 | 411 | 412 | # 413 | # Check for setregid(). 414 | # 415 | src=' 416 | #include 417 | int main(void) { 418 | setregid(0, 0); 419 | return 0; 420 | }' 421 | check_func "setregid" "$src" 422 | 423 | # 424 | # Check for closefrom(). 425 | # 426 | src=' 427 | #include 428 | int main(void) { 429 | closefrom(0); 430 | return 0; 431 | }' 432 | check_func "closefrom" "$src" || { 433 | printf 'SRCS += libopenbsd/closefrom.c\n' >>$CONFIG_MK 434 | } 435 | 436 | # 437 | # Check for sysconf(). 438 | # 439 | src=' 440 | #include 441 | int main(void) { 442 | (void)sysconf(0); 443 | return 0; 444 | }' 445 | check_func "sysconf" "$src" 446 | 447 | # 448 | # Check for dirfd(). 449 | # 450 | src=' 451 | #include 452 | int main(void) { 453 | (void)dirfd(0); 454 | return 0; 455 | }' 456 | check_func "dirfd" "$src" 457 | 458 | # 459 | # Check for fcntl.h. 460 | # 461 | src=' 462 | #include 463 | int main(void) { 464 | return 0; 465 | }' 466 | check_func "fcntl_h" "$src" 467 | 468 | # 469 | # Check for F_CLOSEM. 470 | # 471 | src=' 472 | #include 473 | #ifndef F_CLOSEM 474 | #error no F_CLOSEM 475 | #endif 476 | int main(void) { 477 | return 0; 478 | }' 479 | check_func "F_CLOSEM" "$src" 480 | 481 | # 482 | # Check for dirent.h. 483 | # 484 | src=' 485 | #include 486 | int main(void) { 487 | return 0; 488 | }' 489 | check_func "dirent_h" "$src" 490 | 491 | # 492 | # Check for sys/ndir.h. 493 | # 494 | src=' 495 | #include 496 | int main(void) { 497 | return 0; 498 | }' 499 | check_func "sys_ndir_h" "$src" 500 | 501 | # 502 | # Check for sys/dir.h. 503 | # 504 | src=' 505 | #include 506 | int main(void) { 507 | return 0; 508 | }' 509 | check_func "sys_dir_h" "$src" 510 | 511 | # 512 | # Check for ndir.h. 513 | # 514 | src=' 515 | #include 516 | int main(void) { 517 | return 0; 518 | }' 519 | check_func "ndir_h" "$src" 520 | 521 | # 522 | # Check for login_cap.h. 523 | # 524 | src=' 525 | #include 526 | #include 527 | int main(void) { 528 | return 0; 529 | }' 530 | check_func "login_cap_h" "$src" 531 | 532 | # 533 | # 534 | # 535 | src=' 536 | #include 537 | int main(void){return 0;} 538 | __attribute__((__unused__)) static void foo(void){return;} 539 | ' 540 | check_func "__attribute__" "$src" || { 541 | printf 'OS_CFLAGS += -DNO_ATTRIBUTE_ON_RETURN_TYPE=1\n' >>$CONFIG_MK 542 | } 543 | 544 | auth=$(authmethod) 545 | if [ $? -eq 0 ]; then 546 | printf 'Using auth method\t\t\t%s.\n' "$auth" >&2 547 | else 548 | printf 'Error auth method\t\t\n' >&2 549 | exit 1 550 | fi 551 | 552 | persist=$(persistmethod) 553 | if [ $? -eq 0 ]; then 554 | printf 'Using persist method\t\t\t%s.\n' "$persist" >&2 555 | else 556 | printf 'Using persist method\t\t\tnone.\n' >&2 557 | fi 558 | 559 | printf '#define DOAS_CONF "%s/doas.conf"\n' "${SYSCONFDIR}" >>$CONFIG_H 560 | 561 | printf '\n#endif /* CONFIG_H */\n' >>$CONFIG_H 562 | -------------------------------------------------------------------------------- /doas.1: -------------------------------------------------------------------------------- 1 | .\" $OpenBSD: doas.1,v 1.25 2021/01/16 09:18:41 martijn Exp $ 2 | .\" 3 | .\"Copyright (c) 2015 Ted Unangst 4 | .\" 5 | .\"Permission to use, copy, modify, and distribute this software for any 6 | .\"purpose with or without fee is hereby granted, provided that the above 7 | .\"copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\"THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | .\"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | .\"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .Dd $Mdocdate: January 16 2021 $ 17 | .Dt DOAS 1 18 | .Os 19 | .Sh NAME 20 | .Nm doas 21 | .Nd execute commands as another user 22 | .Sh SYNOPSIS 23 | .Nm doas 24 | .Op Fl Lns 25 | .Op Fl C Ar config 26 | .Op Fl u Ar user 27 | .Ar command 28 | .Op Ar args 29 | .Sh DESCRIPTION 30 | The 31 | .Nm 32 | utility executes the given command as another user. 33 | The 34 | .Ar command 35 | argument is mandatory unless 36 | .Fl C , 37 | .Fl L , 38 | or 39 | .Fl s 40 | is specified. 41 | .Pp 42 | The user will be required to authenticate by entering their password, 43 | unless configured otherwise. 44 | .Pp 45 | By default, a new environment is created. 46 | The variables 47 | .Ev HOME , 48 | .Ev LOGNAME , 49 | .Ev PATH , 50 | .Ev SHELL , 51 | and 52 | .Ev USER 53 | and the 54 | .Xr umask 2 55 | are set to values appropriate for the target user. 56 | .Ev DOAS_USER 57 | is set to the name of the user executing 58 | .Nm . 59 | The variables 60 | .Ev DISPLAY 61 | and 62 | .Ev TERM 63 | are inherited from the current environment. 64 | This behavior may be modified by the config file. 65 | The working directory is not changed. 66 | .Pp 67 | The options are as follows: 68 | .Bl -tag -width tenletters 69 | .It Fl C Ar config 70 | Parse and check the configuration file 71 | .Ar config , 72 | then exit. 73 | If 74 | .Ar command 75 | is supplied, 76 | .Nm 77 | will also perform command matching. 78 | In the latter case 79 | either 80 | .Sq permit , 81 | .Sq permit nopass 82 | or 83 | .Sq deny 84 | will be printed on standard output, depending on command 85 | matching results. 86 | No command is executed. 87 | .It Fl L 88 | Clear any persisted authentications from previous invocations, 89 | then immediately exit. 90 | No command is executed. 91 | .It Fl n 92 | Non interactive mode, fail if the matching rule doesn't have the 93 | .Ic nopass 94 | option. 95 | .It Fl s 96 | Execute the shell from 97 | .Ev SHELL 98 | or 99 | .Pa /etc/passwd . 100 | .It Fl u Ar user 101 | Execute the command as 102 | .Ar user . 103 | The default is root. 104 | .El 105 | .Sh EXIT STATUS 106 | .Ex -std doas 107 | It may fail for one of the following reasons: 108 | .Pp 109 | .Bl -bullet -compact 110 | .It 111 | The config file 112 | .Pa /etc/doas.conf 113 | could not be parsed. 114 | .It 115 | The user attempted to run a command which is not permitted. 116 | .It 117 | The password was incorrect. 118 | .It 119 | The specified command was not found or is not executable. 120 | .El 121 | .Sh SEE ALSO 122 | .Xr su 1 , 123 | .Xr doas.conf 5 124 | .Sh HISTORY 125 | The 126 | .Nm 127 | command first appeared in 128 | .Ox 5.8 . 129 | .Sh AUTHORS 130 | .An Ted Unangst Aq Mt tedu@openbsd.org 131 | -------------------------------------------------------------------------------- /doas.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: doas.c,v 1.52 2016/04/28 04:48:56 tedu Exp $ */ 2 | /* 3 | * Copyright (c) 2015 Ted Unangst 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include "config.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #ifdef HAVE_LOGIN_CAP_H 26 | #include 27 | #endif 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "openbsd.h" 40 | #include "doas.h" 41 | 42 | static void __dead 43 | usage(void) 44 | { 45 | fprintf(stderr, "usage: doas [-Lns] [-C config] [-u user]" 46 | " command [args]\n"); 47 | exit(1); 48 | } 49 | 50 | static int 51 | parseuid(const char *s, uid_t *uid) 52 | { 53 | struct passwd *pw; 54 | const char *errstr; 55 | 56 | if ((pw = getpwnam(s)) != NULL) { 57 | *uid = pw->pw_uid; 58 | if (*uid == UID_MAX) 59 | return -1; 60 | return 0; 61 | } 62 | *uid = strtonum(s, 0, UID_MAX - 1, &errstr); 63 | if (errstr) 64 | return -1; 65 | return 0; 66 | } 67 | 68 | static int 69 | uidcheck(const char *s, uid_t desired) 70 | { 71 | uid_t uid; 72 | 73 | if (parseuid(s, &uid) != 0) 74 | return -1; 75 | if (uid != desired) 76 | return -1; 77 | return 0; 78 | } 79 | 80 | static int 81 | parsegid(const char *s, gid_t *gid) 82 | { 83 | struct group *gr; 84 | const char *errstr; 85 | 86 | if ((gr = getgrnam(s)) != NULL) { 87 | *gid = gr->gr_gid; 88 | if (*gid == GID_MAX) 89 | return -1; 90 | return 0; 91 | } 92 | *gid = strtonum(s, 0, GID_MAX - 1, &errstr); 93 | if (errstr) 94 | return -1; 95 | return 0; 96 | } 97 | 98 | static int 99 | match(uid_t uid, gid_t *groups, int ngroups, uid_t target, const char *cmd, 100 | const char **cmdargs, struct rule *r) 101 | { 102 | int i; 103 | 104 | if (r->ident[0] == ':') { 105 | gid_t rgid; 106 | if (parsegid(r->ident + 1, &rgid) == -1) 107 | return 0; 108 | for (i = 0; i < ngroups; i++) { 109 | if (rgid == groups[i]) 110 | break; 111 | } 112 | if (i == ngroups) 113 | return 0; 114 | } else { 115 | if (uidcheck(r->ident, uid) != 0) 116 | return 0; 117 | } 118 | if (r->target && uidcheck(r->target, target) != 0) 119 | return 0; 120 | if (r->cmd) { 121 | if (strcmp(r->cmd, cmd)) 122 | return 0; 123 | if (r->cmdargs) { 124 | /* if arguments were given, they should match explicitly */ 125 | for (i = 0; r->cmdargs[i]; i++) { 126 | if (!cmdargs[i]) 127 | return 0; 128 | if (strcmp(r->cmdargs[i], cmdargs[i])) 129 | return 0; 130 | } 131 | if (cmdargs[i]) 132 | return 0; 133 | } 134 | } 135 | return 1; 136 | } 137 | 138 | static int 139 | permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr, 140 | uid_t target, const char *cmd, const char **cmdargs) 141 | { 142 | size_t i; 143 | 144 | *lastr = NULL; 145 | for (i = 0; i < nrules; i++) { 146 | if (match(uid, groups, ngroups, target, cmd, 147 | cmdargs, rules[i])) 148 | *lastr = rules[i]; 149 | } 150 | if (!*lastr) 151 | return 0; 152 | return (*lastr)->action == PERMIT; 153 | } 154 | 155 | static void 156 | parseconfig(const char *filename, int checkperms) 157 | { 158 | extern FILE *yyfp; 159 | extern int yyparse(void); 160 | struct stat sb; 161 | 162 | yyfp = fopen(filename, "r"); 163 | if (!yyfp) 164 | err(1, checkperms ? "doas is not enabled, %s" : 165 | "could not open config file %s", filename); 166 | 167 | if (checkperms) { 168 | if (fstat(fileno(yyfp), &sb) != 0) 169 | err(1, "fstat(\"%s\")", filename); 170 | if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) 171 | errx(1, "%s is writable by group or other", filename); 172 | if (sb.st_uid != 0) 173 | errx(1, "%s is not owned by root", filename); 174 | } 175 | 176 | yyparse(); 177 | fclose(yyfp); 178 | if (parse_errors) 179 | exit(1); 180 | } 181 | 182 | static void __dead 183 | checkconfig(const char *confpath, int argc, char **argv, 184 | uid_t uid, gid_t *groups, int ngroups, uid_t target) 185 | { 186 | const struct rule *rule; 187 | 188 | if (setresuid(uid, uid, uid) != 0) 189 | err(1, "setresuid"); 190 | 191 | parseconfig(confpath, 0); 192 | if (!argc) 193 | exit(0); 194 | 195 | if (permit(uid, groups, ngroups, &rule, target, argv[0], 196 | (const char **)argv + 1)) { 197 | printf("permit%s\n", (rule->options & NOPASS) ? " nopass" : ""); 198 | exit(0); 199 | } else { 200 | printf("deny\n"); 201 | exit(1); 202 | } 203 | } 204 | 205 | int 206 | mygetpwuid_r(uid_t uid, struct passwd *pwd, struct passwd **result) 207 | { 208 | int rv; 209 | char *buf; 210 | static long pwsz = 0; 211 | size_t buflen; 212 | 213 | *result = NULL; 214 | 215 | if (pwsz == 0) 216 | pwsz = sysconf(_SC_GETPW_R_SIZE_MAX); 217 | 218 | buflen = pwsz > 0 ? pwsz : 1024; 219 | 220 | buf = malloc(buflen); 221 | if (buf == NULL) 222 | return errno; 223 | 224 | while ((rv = getpwuid_r(uid, pwd, buf, buflen, result)) == ERANGE) { 225 | size_t newsz; 226 | newsz = buflen * 2; 227 | if (newsz < buflen) 228 | return rv; 229 | buflen = newsz; 230 | buf = realloc(buf, buflen); 231 | if (buf == NULL) 232 | return errno; 233 | } 234 | 235 | return rv; 236 | } 237 | 238 | int 239 | main(int argc, char **argv) 240 | { 241 | const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:" 242 | "/usr/local/bin:/usr/local/sbin"; 243 | const char *confpath = NULL; 244 | char *shargv[] = { NULL, NULL }; 245 | char *sh; 246 | const char *p; 247 | const char *cmd; 248 | char cmdline[LINE_MAX]; 249 | struct passwd mypwstore, targpwstore; 250 | struct passwd *mypw, *targpw; 251 | const struct rule *rule; 252 | uid_t uid; 253 | uid_t target = 0; 254 | gid_t groups[NGROUPS_MAX + 1]; 255 | int ngroups; 256 | int i, ch, rv; 257 | int sflag = 0; 258 | int nflag = 0; 259 | char cwdpath[PATH_MAX]; 260 | const char *cwd; 261 | char **envp; 262 | 263 | setprogname("doas"); 264 | 265 | closefrom(STDERR_FILENO + 1); 266 | 267 | uid = getuid(); 268 | 269 | while ((ch = getopt(argc, argv, "+C:Lnsu:")) != -1) { 270 | switch (ch) { 271 | case 'C': 272 | confpath = optarg; 273 | break; 274 | case 'L': 275 | #if defined(USE_TIMESTAMP) 276 | exit(timestamp_clear() == -1); 277 | #else 278 | exit(0); 279 | #endif 280 | case 'u': 281 | if (parseuid(optarg, &target) != 0) 282 | errx(1, "unknown user"); 283 | break; 284 | case 'n': 285 | nflag = 1; 286 | break; 287 | case 's': 288 | sflag = 1; 289 | break; 290 | default: 291 | usage(); 292 | break; 293 | } 294 | } 295 | argv += optind; 296 | argc -= optind; 297 | 298 | if (confpath) { 299 | if (sflag) 300 | usage(); 301 | } else if ((!sflag && !argc) || (sflag && argc)) 302 | usage(); 303 | 304 | rv = mygetpwuid_r(uid, &mypwstore, &mypw); 305 | if (rv != 0) 306 | err(1, "getpwuid_r failed"); 307 | if (mypw == NULL) 308 | errx(1, "no passwd entry for self"); 309 | ngroups = getgroups(NGROUPS_MAX, groups); 310 | if (ngroups == -1) 311 | err(1, "can't get groups"); 312 | groups[ngroups++] = getgid(); 313 | 314 | if (sflag) { 315 | sh = getenv("SHELL"); 316 | if (sh == NULL || *sh == '\0') { 317 | shargv[0] = mypw->pw_shell; 318 | } else 319 | shargv[0] = sh; 320 | argv = shargv; 321 | argc = 1; 322 | } 323 | 324 | if (confpath) { 325 | checkconfig(confpath, argc, argv, uid, groups, ngroups, 326 | target); 327 | exit(1); /* fail safe */ 328 | } 329 | 330 | if (geteuid()) 331 | errx(1, "not installed setuid"); 332 | 333 | parseconfig(DOAS_CONF, 1); 334 | 335 | /* cmdline is used only for logging, no need to abort on truncate */ 336 | (void)strlcpy(cmdline, argv[0], sizeof(cmdline)); 337 | for (i = 1; i < argc; i++) { 338 | if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline)) 339 | break; 340 | if (strlcat(cmdline, argv[i], sizeof(cmdline)) >= sizeof(cmdline)) 341 | break; 342 | } 343 | 344 | cmd = argv[0]; 345 | if (!permit(uid, groups, ngroups, &rule, target, cmd, 346 | (const char **)argv + 1)) { 347 | syslog(LOG_AUTHPRIV | LOG_NOTICE, 348 | "command not permitted for %s: %s", mypw->pw_name, cmdline); 349 | errc(1, EPERM, NULL); 350 | } 351 | 352 | #if defined(USE_SHADOW) 353 | if (!(rule->options & NOPASS)) { 354 | if (nflag) 355 | errx(1, "Authentication required"); 356 | 357 | shadowauth(mypw->pw_name, rule->options & PERSIST); 358 | } 359 | #elif !defined(USE_PAM) 360 | /* no authentication provider, only allow NOPASS rules */ 361 | (void) nflag; 362 | if (!(rule->options & NOPASS)) 363 | errx(1, "Authentication required"); 364 | #endif 365 | 366 | if ((p = getenv("PATH")) != NULL) 367 | formerpath = strdup(p); 368 | if (formerpath == NULL) 369 | formerpath = ""; 370 | 371 | if (rule->cmd) { 372 | if (setenv("PATH", safepath, 1) == -1) 373 | err(1, "failed to set PATH '%s'", safepath); 374 | } 375 | 376 | rv = mygetpwuid_r(target, &targpwstore, &targpw); 377 | if (rv != 0) 378 | err(1, "getpwuid_r failed"); 379 | if (targpw == NULL) 380 | errx(1, "no passwd entry for target"); 381 | 382 | #if defined(USE_PAM) 383 | pamauth(targpw->pw_name, mypw->pw_name, !nflag, rule->options & NOPASS, 384 | rule->options & PERSIST); 385 | #endif 386 | 387 | #ifdef HAVE_LOGIN_CAP_H 388 | if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP | 389 | LOGIN_SETPATH | 390 | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | 391 | LOGIN_SETUSER) != 0) 392 | errx(1, "failed to set user context for target"); 393 | #else 394 | if (setresgid(targpw->pw_gid, targpw->pw_gid, targpw->pw_gid) != 0) 395 | err(1, "setresgid"); 396 | if (initgroups(targpw->pw_name, targpw->pw_gid) != 0) 397 | err(1, "initgroups"); 398 | if (setresuid(target, target, target) != 0) 399 | err(1, "setresuid"); 400 | if (setenv("PATH", safepath, 1) == -1) 401 | err(1, "failed to set PATH '%s'", safepath); 402 | #endif 403 | 404 | if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) 405 | cwd = "(failed)"; 406 | else 407 | cwd = cwdpath; 408 | 409 | if (!(rule->options & NOLOG)) { 410 | syslog(LOG_AUTHPRIV | LOG_INFO, 411 | "%s ran command %s as %s from %s", 412 | mypw->pw_name, cmdline, targpw->pw_name, cwd); 413 | } 414 | 415 | envp = prepenv(rule, mypw, targpw); 416 | 417 | /* setusercontext set path for the next process, so reset it for us */ 418 | if (rule->cmd) { 419 | if (setenv("PATH", safepath, 1) == -1) 420 | err(1, "failed to set PATH '%s'", safepath); 421 | } else { 422 | if (setenv("PATH", formerpath, 1) == -1) 423 | err(1, "failed to set PATH '%s'", formerpath); 424 | } 425 | execvpe(cmd, argv, envp); 426 | if (errno == ENOENT) 427 | errx(1, "%s: command not found", cmd); 428 | err(1, "%s", cmd); 429 | } 430 | -------------------------------------------------------------------------------- /doas.conf.5: -------------------------------------------------------------------------------- 1 | .\" $OpenBSD: doas.conf.5,v 1.45 2020/10/09 10:24:33 jmc Exp $ 2 | .\" 3 | .\"Copyright (c) 2015 Ted Unangst 4 | .\" 5 | .\"Permission to use, copy, modify, and distribute this software for any 6 | .\"purpose with or without fee is hereby granted, provided that the above 7 | .\"copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\"THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | .\"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | .\"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .Dd $Mdocdate: October 9 2020 $ 17 | .Dt DOAS.CONF 5 18 | .Os 19 | .Sh NAME 20 | .Nm doas.conf 21 | .Nd doas configuration file 22 | .Sh DESCRIPTION 23 | The 24 | .Xr doas 1 25 | utility executes commands as other users according to the rules 26 | in the 27 | .Nm 28 | configuration file. 29 | .Pp 30 | The rules have the following format: 31 | .Bd -ragged -offset indent 32 | .Ic permit Ns | Ns Ic deny 33 | .Op Ar options 34 | .Ar identity 35 | .Op Ic as Ar target 36 | .Op Ic cmd Ar command Op Ic args No ... 37 | .Ed 38 | .Pp 39 | Rules consist of the following parts: 40 | .Bl -tag -width 11n 41 | .It Ic permit Ns | Ns Ic deny 42 | The action to be taken if this rule matches. 43 | .It Ar options 44 | Options are: 45 | .Bl -tag -width keepenv 46 | .It Ic nopass 47 | The user is not required to enter a password. 48 | .It Ic nolog 49 | Do not log successful command execution to 50 | .Xr syslogd 8 . 51 | .It Ic persist 52 | After the user successfully authenticates, do not ask for a password 53 | again for some time. 54 | .It Ic keepenv 55 | Environment variables other than those listed in 56 | .Xr doas 1 57 | are retained when creating the environment for the new process. 58 | .It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic } 59 | Keep or set the space-separated specified variables. 60 | Variables may also be removed with a leading 61 | .Sq - 62 | or set using the latter syntax. 63 | If the first character of 64 | .Ar value 65 | is a 66 | .Ql $ 67 | then the value to be set is taken from the existing environment 68 | variable of the indicated name. 69 | This option is processed after the default environment has been created. 70 | .El 71 | .It Ar identity 72 | The username to match. 73 | Groups may be specified by prepending a colon 74 | .Pq Sq \&: . 75 | Numeric IDs are also accepted. 76 | .It Ic as Ar target 77 | The target user the running user is allowed to run the command as. 78 | The default is all users. 79 | .It Ic cmd Ar command 80 | The command the user is allowed or denied to run. 81 | The default is all commands. 82 | Be advised that it is best to specify absolute paths. 83 | If a relative path is specified, only a restricted 84 | .Ev PATH 85 | will be searched. 86 | .It Ic args Op Ar argument ... 87 | Arguments to command. 88 | The command arguments provided by the user need to match those specified. 89 | The keyword 90 | .Ic args 91 | alone means that command must be run without any arguments. 92 | .El 93 | .Pp 94 | The last matching rule determines the action taken. 95 | If no rule matches, the action is denied. 96 | .Pp 97 | Comments can be put anywhere in the file using a hash mark 98 | .Pq Sq # , 99 | and extend to the end of the current line. 100 | .Pp 101 | The following quoting rules apply: 102 | .Bl -dash 103 | .It 104 | The text between a pair of double quotes 105 | .Pq Sq \&" 106 | is taken as is. 107 | .It 108 | The backslash character 109 | .Pq Sq \e 110 | escapes the next character, including new line characters, outside comments; 111 | as a result, comments may not be extended over multiple lines. 112 | .It 113 | If quotes or backslashes are used in a word, 114 | it is not considered a keyword. 115 | .El 116 | .Sh FILES 117 | .Bl -tag -width /etc/examples/doas.conf -compact 118 | .It Pa /etc/doas.conf 119 | .Xr doas 1 120 | configuration file. 121 | .It Pa /etc/examples/doas.conf 122 | Example configuration file. 123 | .El 124 | .Sh EXAMPLES 125 | The following example permits user aja to install packages 126 | from a preferred mirror; 127 | group wheel to execute commands as any user while keeping the environment 128 | variables 129 | .Ev PS1 130 | and 131 | .Ev SSH_AUTH_SOCK 132 | and 133 | unsetting 134 | .Ev ENV ; 135 | permits tedu to run procmap as root without a password; 136 | and additionally permits root to run unrestricted commands as itself 137 | while retaining the original PATH. 138 | .Bd -literal -offset indent 139 | permit persist setenv { PKG_CACHE PKG_PATH } aja cmd pkg_add 140 | permit setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel 141 | permit nopass tedu as root cmd /usr/sbin/procmap 142 | permit nopass keepenv setenv { PATH } root as root 143 | .Ed 144 | .Sh SEE ALSO 145 | .Xr doas 1 , 146 | .Xr syslogd 8 147 | .Sh HISTORY 148 | The 149 | .Nm 150 | configuration file first appeared in 151 | .Ox 5.8 . 152 | .Sh AUTHORS 153 | .An Ted Unangst Aq Mt tedu@openbsd.org 154 | -------------------------------------------------------------------------------- /doas.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD$ */ 2 | /* 3 | * Copyright (c) 2015 Ted Unangst 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | struct rule { 19 | int action; 20 | int options; 21 | const char *ident; 22 | const char *target; 23 | const char *cmd; 24 | const char **cmdargs; 25 | const char **envlist; 26 | }; 27 | 28 | extern struct rule **rules; 29 | extern size_t nrules; 30 | extern int parse_errors; 31 | 32 | extern const char *formerpath; 33 | 34 | struct passwd; 35 | 36 | char **prepenv(const struct rule *, const struct passwd *, 37 | const struct passwd *); 38 | 39 | #define PERMIT 1 40 | #define DENY 2 41 | 42 | #define NOPASS 0x1 43 | #define KEEPENV 0x2 44 | #define PERSIST 0x4 45 | #define NOLOG 0x8 46 | 47 | #ifdef USE_PAM 48 | void pamauth(const char *, const char *, int, int, int); 49 | #endif 50 | 51 | #ifdef USE_SHADOW 52 | void shadowauth(const char *, int); 53 | #endif 54 | 55 | #ifdef USE_TIMESTAMP 56 | int timestamp_open(int *, int); 57 | int timestamp_set(int, int); 58 | int timestamp_clear(void); 59 | #endif 60 | -------------------------------------------------------------------------------- /env.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD$ */ 2 | /* 3 | * Copyright (c) 2016 Ted Unangst 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include "config.h" 19 | 20 | #include 21 | #include "sys-tree.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "openbsd.h" 32 | #include "doas.h" 33 | 34 | const char *formerpath; 35 | 36 | struct envnode { 37 | RB_ENTRY(envnode) node; 38 | const char *key; 39 | const char *value; 40 | }; 41 | 42 | struct env { 43 | RB_HEAD(envtree, envnode) root; 44 | u_int count; 45 | }; 46 | 47 | static void fillenv(struct env *env, const char **envlist); 48 | 49 | static int 50 | envcmp(struct envnode *a, struct envnode *b) 51 | { 52 | return strcmp(a->key, b->key); 53 | } 54 | RB_GENERATE_STATIC(envtree, envnode, node, envcmp) 55 | 56 | static struct envnode * 57 | createnode(const char *key, const char *value) 58 | { 59 | struct envnode *node; 60 | 61 | node = malloc(sizeof(*node)); 62 | if (!node) 63 | err(1, NULL); 64 | node->key = strdup(key); 65 | node->value = strdup(value); 66 | if (!node->key || !node->value) 67 | err(1, NULL); 68 | return node; 69 | } 70 | 71 | static void 72 | freenode(struct envnode *node) 73 | { 74 | free((char *)node->key); 75 | free((char *)node->value); 76 | free(node); 77 | } 78 | 79 | static void 80 | addnode(struct env *env, const char *key, const char *value) 81 | { 82 | struct envnode *node; 83 | 84 | node = createnode(key, value); 85 | RB_INSERT(envtree, &env->root, node); 86 | env->count++; 87 | } 88 | 89 | static struct env * 90 | createenv(const struct rule *rule, const struct passwd *mypw, 91 | const struct passwd *targpw) 92 | { 93 | static const char *copyset[] = { 94 | "DISPLAY", "TERM", 95 | NULL 96 | }; 97 | struct env *env; 98 | u_int i; 99 | 100 | env = malloc(sizeof(*env)); 101 | if (!env) 102 | err(1, NULL); 103 | RB_INIT(&env->root); 104 | env->count = 0; 105 | 106 | addnode(env, "DOAS_USER", mypw->pw_name); 107 | addnode(env, "HOME", targpw->pw_dir); 108 | addnode(env, "LOGNAME", targpw->pw_name); 109 | addnode(env, "PATH", getenv("PATH")); 110 | addnode(env, "SHELL", targpw->pw_shell); 111 | addnode(env, "USER", targpw->pw_name); 112 | 113 | fillenv(env, copyset); 114 | 115 | if (rule->options & KEEPENV) { 116 | extern char **environ; 117 | 118 | for (i = 0; environ[i] != NULL; i++) { 119 | struct envnode *node; 120 | const char *e, *eq; 121 | size_t len; 122 | char keybuf[1024]; 123 | 124 | e = environ[i]; 125 | 126 | /* ignore invalid or overlong names */ 127 | if ((eq = strchr(e, '=')) == NULL || eq == e) 128 | continue; 129 | len = eq - e; 130 | if (len > sizeof(keybuf) - 1) 131 | continue; 132 | memcpy(keybuf, e, len); 133 | keybuf[len] = '\0'; 134 | 135 | node = createnode(keybuf, eq + 1); 136 | if (RB_INSERT(envtree, &env->root, node)) { 137 | /* ignore any later duplicates */ 138 | freenode(node); 139 | } else { 140 | env->count++; 141 | } 142 | } 143 | } 144 | 145 | return env; 146 | } 147 | 148 | static char ** 149 | flattenenv(struct env *env) 150 | { 151 | char **envp; 152 | struct envnode *node; 153 | u_int i; 154 | 155 | envp = reallocarray(NULL, env->count + 1, sizeof(char *)); 156 | if (!envp) 157 | err(1, NULL); 158 | i = 0; 159 | RB_FOREACH(node, envtree, &env->root) { 160 | if (asprintf(&envp[i], "%s=%s", node->key, node->value) == -1) 161 | err(1, NULL); 162 | i++; 163 | } 164 | envp[i] = NULL; 165 | return envp; 166 | } 167 | 168 | static void 169 | fillenv(struct env *env, const char **envlist) 170 | { 171 | struct envnode *node, key; 172 | const char *e, *eq; 173 | const char *val; 174 | char name[1024]; 175 | u_int i; 176 | size_t len; 177 | 178 | for (i = 0; envlist[i]; i++) { 179 | e = envlist[i]; 180 | 181 | /* parse out env name */ 182 | if ((eq = strchr(e, '=')) == NULL) 183 | len = strlen(e); 184 | else 185 | len = eq - e; 186 | if (len > sizeof(name) - 1) 187 | continue; 188 | memcpy(name, e, len); 189 | name[len] = '\0'; 190 | 191 | /* delete previous copies */ 192 | key.key = name; 193 | if (*name == '-') 194 | key.key = name + 1; 195 | if ((node = RB_FIND(envtree, &env->root, &key))) { 196 | RB_REMOVE(envtree, &env->root, node); 197 | freenode(node); 198 | env->count--; 199 | } 200 | if (*name == '-') 201 | continue; 202 | 203 | /* assign value or inherit from environ */ 204 | if (eq) { 205 | val = eq + 1; 206 | if (*val == '$') { 207 | if (strcmp(val + 1, "PATH") == 0) 208 | val = formerpath; 209 | else 210 | val = getenv(val + 1); 211 | } 212 | } else { 213 | if (strcmp(name, "PATH") == 0) 214 | val = formerpath; 215 | else 216 | val = getenv(name); 217 | } 218 | /* at last, we have something to insert */ 219 | if (val) { 220 | node = createnode(name, val); 221 | RB_INSERT(envtree, &env->root, node); 222 | env->count++; 223 | } 224 | } 225 | } 226 | 227 | char ** 228 | prepenv(const struct rule *rule, const struct passwd *mypw, 229 | const struct passwd *targpw) 230 | { 231 | struct env *env; 232 | 233 | env = createenv(rule, mypw, targpw); 234 | if (rule->envlist) 235 | fillenv(env, rule->envlist); 236 | 237 | return flattenenv(env); 238 | } 239 | -------------------------------------------------------------------------------- /libopenbsd/bsd-setres_id.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Darren Tucker (dtucker at zip com au). 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "config.h" 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if !defined(HAVE_SETRESGID) || defined(BROKEN_SETRESGID) 27 | int 28 | setresgid(gid_t rgid, gid_t egid, gid_t sgid) 29 | { 30 | int ret = 0; 31 | 32 | if (rgid != sgid) { 33 | errno = ENOSYS; 34 | return -1; 35 | } 36 | #if defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID) 37 | if (setregid(rgid, egid) < 0) { 38 | ret = -1; 39 | } 40 | #else 41 | if (setegid(egid) < 0) { 42 | ret = -1; 43 | } 44 | if (setgid(rgid) < 0) { 45 | ret = -1; 46 | } 47 | #endif 48 | return ret; 49 | } 50 | #endif 51 | 52 | #if !defined(HAVE_SETRESUID) || defined(BROKEN_SETRESUID) 53 | int 54 | setresuid(uid_t ruid, uid_t euid, uid_t suid) 55 | { 56 | int ret = 0; 57 | 58 | if (ruid != suid) { 59 | errno = ENOSYS; 60 | return -1; 61 | } 62 | #if defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID) 63 | if (setreuid(ruid, euid) < 0) { 64 | ret = -1; 65 | } 66 | #else 67 | 68 | # ifndef SETEUID_BREAKS_SETUID 69 | if (seteuid(euid) < 0) { 70 | ret = -1; 71 | } 72 | # endif 73 | if (setuid(ruid) < 0) { 74 | ret = -1; 75 | } 76 | #endif 77 | return ret; 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /libopenbsd/closefrom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: ISC 3 | * 4 | * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2018 5 | * Todd C. Miller 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 THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | /* 21 | * This is an open source non-commercial project. Dear PVS-Studio, please check it. 22 | * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com 23 | */ 24 | 25 | #include "config.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #ifdef HAVE_PSTAT_GETPROC 35 | # include 36 | #else 37 | # include 38 | #endif 39 | 40 | #include "openbsd.h" 41 | 42 | #ifndef _POSIX_OPEN_MAX 43 | # define _POSIX_OPEN_MAX 20 44 | #endif 45 | 46 | /* 47 | * Close all file descriptors greater than or equal to lowfd. 48 | * This is the expensive (fallback) method. 49 | */ 50 | static void 51 | closefrom_fallback(int lowfd) 52 | { 53 | long fd, maxfd; 54 | 55 | /* 56 | * Fall back on sysconf(_SC_OPEN_MAX). We avoid checking 57 | * resource limits since it is possible to open a file descriptor 58 | * and then drop the rlimit such that it is below the open fd. 59 | */ 60 | maxfd = sysconf(_SC_OPEN_MAX); 61 | if (maxfd < 0) 62 | maxfd = _POSIX_OPEN_MAX; 63 | 64 | for (fd = lowfd; fd < maxfd; fd++) { 65 | #ifdef __APPLE__ 66 | /* Avoid potential libdispatch crash when we close its fds. */ 67 | (void) fcntl((int) fd, F_SETFD, FD_CLOEXEC); 68 | #else 69 | (void) close((int) fd); 70 | #endif 71 | } 72 | } 73 | 74 | /* 75 | * Close all file descriptors greater than or equal to lowfd. 76 | * We try the fast way first, falling back on the slow method. 77 | */ 78 | void 79 | closefrom(int lowfd) 80 | { 81 | #if defined(HAVE_PSTAT_GETPROC) 82 | struct pst_status pstat; 83 | #elif defined(HAVE_DIRFD) 84 | const char *path; 85 | DIR *dirp; 86 | #endif 87 | 88 | /* Try the fast method first, if possible. */ 89 | #if defined(HAVE_FCNTL_CLOSEM) 90 | if (fcntl(lowfd, F_CLOSEM, 0) != -1) 91 | return; 92 | #endif 93 | #if defined(HAVE_PSTAT_GETPROC) 94 | /* 95 | * EOVERFLOW is not a fatal error for the fields we use. 96 | * See the "EOVERFLOW Error" section of pstat_getvminfo(3). 97 | */ 98 | if (pstat_getproc(&pstat, sizeof(pstat), 0, getpid()) != -1 || 99 | errno == EOVERFLOW) { 100 | int fd; 101 | 102 | for (fd = lowfd; fd <= pstat.pst_highestfd; fd++) 103 | (void) close(fd); 104 | return; 105 | } 106 | #elif defined(HAVE_DIRFD) 107 | /* Use /proc/self/fd (or /dev/fd on macOS) if it exists. */ 108 | # ifdef __APPLE__ 109 | path = _PATH_DEV "fd"; 110 | # else 111 | path = "/proc/self/fd"; 112 | # endif 113 | if ((dirp = opendir(path)) != NULL) { 114 | struct dirent *dent; 115 | while ((dent = readdir(dirp)) != NULL) { 116 | const char *errstr; 117 | int fd = strtonum(dent->d_name, lowfd, INT_MAX, &errstr); 118 | if (errstr == NULL && fd != dirfd(dirp)) { 119 | # ifdef __APPLE__ 120 | /* Avoid potential libdispatch crash when we close its fds. */ 121 | (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 122 | # else 123 | (void) close(fd); 124 | # endif 125 | } 126 | } 127 | (void) closedir(dirp); 128 | return; 129 | } 130 | #endif /* HAVE_DIRFD */ 131 | 132 | /* Do things the slow way. */ 133 | closefrom_fallback(lowfd); 134 | } 135 | -------------------------------------------------------------------------------- /libopenbsd/errc.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: errc.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */ 2 | /*- 3 | * Copyright (c) 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "config.h" 32 | 33 | #include 34 | #include 35 | 36 | #include "openbsd.h" 37 | 38 | void __dead 39 | errc(int eval, int code, const char *fmt, ...) 40 | { 41 | va_list ap; 42 | 43 | va_start(ap, fmt); 44 | verrc(eval, code, fmt, ap); 45 | va_end(ap); 46 | } 47 | -------------------------------------------------------------------------------- /libopenbsd/execvpe.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: exec.c,v 1.23 2016/03/13 18:34:20 guenther Exp $ */ 2 | /*- 3 | * Copyright (c) 1991, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "config.h" 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "openbsd.h" 46 | 47 | int 48 | execvpe(const char *name, char *const *argv, char *const *envp) 49 | { 50 | char **memp; 51 | int cnt; 52 | size_t lp, ln, len; 53 | char *p; 54 | int eacces = 0; 55 | char *bp, *cur, *path, buf[PATH_MAX]; 56 | 57 | /* 58 | * Do not allow null name 59 | */ 60 | if (name == NULL || *name == '\0') { 61 | errno = ENOENT; 62 | return (-1); 63 | } 64 | 65 | /* If it's an absolute or relative path name, it's easy. */ 66 | if (strchr(name, '/')) { 67 | bp = (char *)name; 68 | cur = path = NULL; 69 | goto retry; 70 | } 71 | bp = buf; 72 | 73 | /* Get the path we're searching. */ 74 | if (!(path = getenv("PATH"))) 75 | path = _PATH_DEFPATH; 76 | len = strlen(path) + 1; 77 | cur = alloca(len); 78 | if (cur == NULL) { 79 | errno = ENOMEM; 80 | return (-1); 81 | } 82 | strlcpy(cur, path, len); 83 | path = cur; 84 | while ((p = strsep(&cur, ":"))) { 85 | /* 86 | * It's a SHELL path -- double, leading and trailing colons 87 | * mean the current directory. 88 | */ 89 | if (!*p) { 90 | p = "."; 91 | lp = 1; 92 | } else 93 | lp = strlen(p); 94 | ln = strlen(name); 95 | 96 | /* 97 | * If the path is too long complain. This is a possible 98 | * security issue; given a way to make the path too long 99 | * the user may execute the wrong program. 100 | */ 101 | if (lp + ln + 2 > sizeof(buf)) { 102 | struct iovec iov[3]; 103 | 104 | iov[0].iov_base = "execvp: "; 105 | iov[0].iov_len = 8; 106 | iov[1].iov_base = p; 107 | iov[1].iov_len = lp; 108 | iov[2].iov_base = ": path too long\n"; 109 | iov[2].iov_len = 16; 110 | (void)writev(STDERR_FILENO, iov, 3); 111 | continue; 112 | } 113 | bcopy(p, buf, lp); 114 | buf[lp] = '/'; 115 | bcopy(name, buf + lp + 1, ln); 116 | buf[lp + ln + 1] = '\0'; 117 | 118 | retry: (void)execve(bp, argv, envp); 119 | switch(errno) { 120 | case E2BIG: 121 | goto done; 122 | case EISDIR: 123 | case ELOOP: 124 | case ENAMETOOLONG: 125 | case ENOENT: 126 | break; 127 | case ENOEXEC: 128 | for (cnt = 0; argv[cnt]; ++cnt) 129 | ; 130 | memp = alloca((cnt + 2) * sizeof(char *)); 131 | if (memp == NULL) 132 | goto done; 133 | memp[0] = "sh"; 134 | memp[1] = bp; 135 | bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); 136 | (void)execve(_PATH_BSHELL, memp, envp); 137 | goto done; 138 | case ENOMEM: 139 | goto done; 140 | case ENOTDIR: 141 | break; 142 | case ETXTBSY: 143 | /* 144 | * We used to retry here, but sh(1) doesn't. 145 | */ 146 | goto done; 147 | case EACCES: 148 | eacces = 1; 149 | break; 150 | default: 151 | goto done; 152 | } 153 | } 154 | if (eacces) 155 | errno = EACCES; 156 | else if (!errno) 157 | errno = ENOENT; 158 | done: 159 | return (-1); 160 | } 161 | -------------------------------------------------------------------------------- /libopenbsd/explicit_bzero.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: explicit_bzero.c,v 1.2 2014/06/10 04:17:37 deraadt Exp $ */ 2 | /* 3 | * Public domain. 4 | * Written by Matthew Dempsky. 5 | */ 6 | 7 | #include "config.h" 8 | 9 | #include 10 | 11 | #define __UNUSED __attribute__ ((unused)) 12 | 13 | __attribute__((weak)) void 14 | __explicit_bzero_hook(__UNUSED void *buf, __UNUSED size_t len) 15 | { 16 | } 17 | 18 | void 19 | explicit_bzero(void *buf, size_t len) 20 | { 21 | memset(buf, 0, len); 22 | __explicit_bzero_hook(buf, len); 23 | } 24 | -------------------------------------------------------------------------------- /libopenbsd/openbsd.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_OPENBSD_H_ 2 | #define _LIB_OPENBSD_H_ 3 | 4 | #include 5 | #include 6 | 7 | #ifndef __UNUSED 8 | # define __UNUSED __attribute__ ((unused)) 9 | #endif 10 | 11 | #ifndef __dead 12 | # define __dead __attribute__ ((noreturn)) 13 | #endif 14 | 15 | /* API definitions lifted from OpenBSD src/include */ 16 | 17 | /* stdlib.h */ 18 | #ifndef HAVE_REALLOCARRAY 19 | void * reallocarray(void *optr, size_t nmemb, size_t size); 20 | #endif /* HAVE_REALLOCARRAY */ 21 | #ifndef HAVE_STRTONUM 22 | long long strtonum(const char *numstr, long long minval, 23 | long long maxval, const char **errstrp); 24 | #endif /* !HAVE_STRTONUM */ 25 | 26 | /* string.h */ 27 | #ifndef HAVE_EXPLICIT_BZERO 28 | void explicit_bzero(void *, size_t); 29 | #endif 30 | #ifndef HAVE_STRLCAT 31 | size_t strlcat(char *dst, const char *src, size_t dsize); 32 | #endif /* !HAVE_STRLCAT */ 33 | #ifndef HAVE_STRLCPY 34 | size_t strlcpy(char *dst, const char *src, size_t dsize); 35 | #endif /* !HAVE_STRLCPY */ 36 | 37 | /* unistd.h */ 38 | #ifndef HAVE_EXECVPE 39 | int execvpe(const char *, char *const *, char *const *); 40 | #endif /* !HAVE_EXECVPE */ 41 | #ifndef HAVE_SETRESUID 42 | int setresuid(uid_t, uid_t, uid_t); 43 | #endif /* !HAVE_SETRESUID */ 44 | #ifndef HAVE_PLEDGE 45 | int pledge(const char *promises, const char *paths[]); 46 | #endif /* !HAVE_PLEDGE */ 47 | #ifndef HAVE_CLOSEFROM 48 | void closefrom(int); 49 | #endif /* !HAVE_CLOSEFROM */ 50 | 51 | /* err.h */ 52 | #ifndef HAVE_VERRC 53 | void __dead verrc(int eval, int code, const char *fmt, va_list ap); 54 | #endif /* !HAVE_VERRC */ 55 | #ifndef HAVE_ERRC 56 | __dead void errc(int eval, int code, const char *fmt, ...); 57 | #endif /* !HAVE_ERRC */ 58 | 59 | #ifndef HAVE_SETPROGNAME 60 | const char * getprogname(void); 61 | void setprogname(const char *progname); 62 | #endif /* !HAVE_SETPROGNAME */ 63 | 64 | #ifndef HAVE_SETRESGID 65 | int setresgid(gid_t, gid_t, gid_t); 66 | #endif 67 | #ifndef HAVE_SETRESUID 68 | int setresuid(uid_t, uid_t, uid_t); 69 | #endif 70 | 71 | #endif /* _LIB_OPENBSD_H_ */ 72 | -------------------------------------------------------------------------------- /libopenbsd/progname.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2006 Robert Millan 3 | * Copyright © 2010-2012 Guillem Jover 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * Rejected in glibc 30 | * . 31 | */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef HAVE___PROGNAME 40 | extern const char *__progname; 41 | #else 42 | static const char *__progname = NULL; 43 | #endif 44 | 45 | const char * 46 | getprogname(void) 47 | { 48 | #if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) 49 | if (__progname == NULL) 50 | __progname = program_invocation_short_name; 51 | #elif defined(HAVE_GETEXECNAME) 52 | /* getexecname(3) returns an absolute pathname, normalize it. */ 53 | if (__progname == NULL) 54 | setprogname(getexecname()); 55 | #endif 56 | 57 | return __progname; 58 | } 59 | 60 | void 61 | setprogname(const char *progname) 62 | { 63 | const char *last_slash; 64 | 65 | last_slash = strrchr(progname, '/'); 66 | if (last_slash == NULL) 67 | __progname = progname; 68 | else 69 | __progname = last_slash + 1; 70 | } 71 | -------------------------------------------------------------------------------- /libopenbsd/readpassphrase.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2000-2002, 2007, 2010 5 | * Todd C. Miller 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 THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | * Sponsored in part by the Defense Advanced Research Projects 20 | * Agency (DARPA) and Air Force Research Laboratory, Air Force 21 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. 22 | */ 23 | 24 | /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ 25 | 26 | #include "config.h" 27 | 28 | #ifndef HAVE_READPASSPHRASE 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "sys-readpassphrase.h" 40 | 41 | #ifndef _PATH_TTY 42 | #define _PATH_TTY "/dev/tty" 43 | #endif 44 | 45 | #ifndef TCSASOFT 46 | /* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */ 47 | # define TCSASOFT 0 48 | #endif 49 | 50 | /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ 51 | #if !defined(_POSIX_VDISABLE) && defined(VDISABLE) 52 | # define _POSIX_VDISABLE VDISABLE 53 | #endif 54 | 55 | static volatile sig_atomic_t signo[_NSIG]; 56 | 57 | static void handler(int); 58 | 59 | char * 60 | readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 61 | { 62 | ssize_t nr; 63 | int input, output, save_errno, i, need_restart; 64 | char ch, *p, *end; 65 | struct termios term, oterm; 66 | struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 67 | struct sigaction savetstp, savettin, savettou, savepipe; 68 | 69 | /* I suppose we could alloc on demand in this case (XXX). */ 70 | if (bufsiz == 0) { 71 | errno = EINVAL; 72 | return(NULL); 73 | } 74 | 75 | restart: 76 | for (i = 0; i < _NSIG; i++) 77 | signo[i] = 0; 78 | nr = -1; 79 | save_errno = 0; 80 | need_restart = 0; 81 | /* 82 | * Read and write to /dev/tty if available. If not, read from 83 | * stdin and write to stderr unless a tty is required. 84 | */ 85 | if ((flags & RPP_STDIN) || 86 | (input = output = open(_PATH_TTY, O_RDWR)) == -1) { 87 | if (flags & RPP_REQUIRE_TTY) { 88 | errno = ENOTTY; 89 | return(NULL); 90 | } 91 | input = STDIN_FILENO; 92 | output = STDERR_FILENO; 93 | } 94 | 95 | /* 96 | * Turn off echo if possible. 97 | * If we are using a tty but are not the foreground pgrp this will 98 | * generate SIGTTOU, so do it *before* installing the signal handlers. 99 | */ 100 | if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 101 | memcpy(&term, &oterm, sizeof(term)); 102 | if (!(flags & RPP_ECHO_ON)) 103 | term.c_lflag &= ~(ECHO | ECHONL); 104 | #ifdef VSTATUS 105 | if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 106 | term.c_cc[VSTATUS] = _POSIX_VDISABLE; 107 | #endif 108 | (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); 109 | } else { 110 | memset(&term, 0, sizeof(term)); 111 | term.c_lflag |= ECHO; 112 | memset(&oterm, 0, sizeof(oterm)); 113 | oterm.c_lflag |= ECHO; 114 | } 115 | 116 | /* 117 | * Catch signals that would otherwise cause the user to end 118 | * up with echo turned off in the shell. Don't worry about 119 | * things like SIGXCPU and SIGVTALRM for now. 120 | */ 121 | sigemptyset(&sa.sa_mask); 122 | sa.sa_flags = 0; /* don't restart system calls */ 123 | sa.sa_handler = handler; 124 | (void)sigaction(SIGALRM, &sa, &savealrm); 125 | (void)sigaction(SIGHUP, &sa, &savehup); 126 | (void)sigaction(SIGINT, &sa, &saveint); 127 | (void)sigaction(SIGPIPE, &sa, &savepipe); 128 | (void)sigaction(SIGQUIT, &sa, &savequit); 129 | (void)sigaction(SIGTERM, &sa, &saveterm); 130 | (void)sigaction(SIGTSTP, &sa, &savetstp); 131 | (void)sigaction(SIGTTIN, &sa, &savettin); 132 | (void)sigaction(SIGTTOU, &sa, &savettou); 133 | 134 | if (!(flags & RPP_STDIN)) 135 | (void)write(output, prompt, strlen(prompt)); 136 | end = buf + bufsiz - 1; 137 | p = buf; 138 | while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 139 | if (p < end) { 140 | if ((flags & RPP_SEVENBIT)) 141 | ch &= 0x7f; 142 | if (isalpha((unsigned char)ch)) { 143 | if ((flags & RPP_FORCELOWER)) 144 | ch = (char)tolower((unsigned char)ch); 145 | if ((flags & RPP_FORCEUPPER)) 146 | ch = (char)toupper((unsigned char)ch); 147 | } 148 | *p++ = ch; 149 | } 150 | } 151 | *p = '\0'; 152 | save_errno = errno; 153 | if (!(term.c_lflag & ECHO)) 154 | (void)write(output, "\n", 1); 155 | 156 | /* Restore old terminal settings and signals. */ 157 | if (memcmp(&term, &oterm, sizeof(term)) != 0) { 158 | const int sigttou = signo[SIGTTOU]; 159 | 160 | /* Ignore SIGTTOU generated when we are not the fg pgrp. */ 161 | while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 && 162 | errno == EINTR && !signo[SIGTTOU]) 163 | continue; 164 | signo[SIGTTOU] = sigttou; 165 | } 166 | (void)sigaction(SIGALRM, &savealrm, NULL); 167 | (void)sigaction(SIGHUP, &savehup, NULL); 168 | (void)sigaction(SIGINT, &saveint, NULL); 169 | (void)sigaction(SIGQUIT, &savequit, NULL); 170 | (void)sigaction(SIGPIPE, &savepipe, NULL); 171 | (void)sigaction(SIGTERM, &saveterm, NULL); 172 | (void)sigaction(SIGTSTP, &savetstp, NULL); 173 | (void)sigaction(SIGTTIN, &savettin, NULL); 174 | (void)sigaction(SIGTTOU, &savettou, NULL); 175 | if (input != STDIN_FILENO) 176 | (void)close(input); 177 | 178 | /* 179 | * If we were interrupted by a signal, resend it to ourselves 180 | * now that we have restored the signal handlers. 181 | */ 182 | for (i = 0; i < _NSIG; i++) { 183 | if (signo[i]) { 184 | kill(getpid(), i); 185 | switch (i) { 186 | case SIGTSTP: 187 | case SIGTTIN: 188 | case SIGTTOU: 189 | need_restart = 1; 190 | } 191 | } 192 | } 193 | if (need_restart) 194 | goto restart; 195 | 196 | if (save_errno) 197 | errno = save_errno; 198 | return(nr == -1 ? NULL : buf); 199 | } 200 | 201 | #if 0 202 | char * 203 | getpass(const char *prompt) 204 | { 205 | static char buf[_PASSWORD_LEN + 1]; 206 | 207 | return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); 208 | } 209 | #endif 210 | 211 | static void handler(int s) 212 | { 213 | 214 | signo[s] = 1; 215 | } 216 | #endif /* HAVE_READPASSPHRASE */ 217 | -------------------------------------------------------------------------------- /libopenbsd/reallocarray.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ 2 | /* 3 | * Copyright (c) 2008 Otto Moerbeek 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include "config.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | /* 26 | * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX 27 | * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW 28 | */ 29 | #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) 30 | 31 | void * 32 | reallocarray(void *optr, size_t nmemb, size_t size) 33 | { 34 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && 35 | nmemb > 0 && SIZE_MAX / nmemb < size) { 36 | errno = ENOMEM; 37 | return NULL; 38 | } 39 | return realloc(optr, size * nmemb); 40 | } 41 | -------------------------------------------------------------------------------- /libopenbsd/strlcat.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcat.c,v 1.16 2015/08/31 02:53:57 guenther Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "config.h" 20 | 21 | #include 22 | #include 23 | 24 | /* 25 | * Appends src to string dst of size dsize (unlike strncat, dsize is the 26 | * full size of dst, not space left). At most dsize-1 characters 27 | * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). 28 | * Returns strlen(src) + MIN(dsize, strlen(initial dst)). 29 | * If retval >= dsize, truncation occurred. 30 | */ 31 | size_t 32 | strlcat(char *dst, const char *src, size_t dsize) 33 | { 34 | const char *odst = dst; 35 | const char *osrc = src; 36 | size_t n = dsize; 37 | size_t dlen; 38 | 39 | /* Find the end of dst and adjust bytes left but don't go past end. */ 40 | while (n-- != 0 && *dst != '\0') 41 | dst++; 42 | dlen = dst - odst; 43 | n = dsize - dlen; 44 | 45 | if (n-- == 0) 46 | return(dlen + strlen(src)); 47 | while (*src != '\0') { 48 | if (n != 0) { 49 | *dst++ = *src; 50 | n--; 51 | } 52 | src++; 53 | } 54 | *dst = '\0'; 55 | 56 | return(dlen + (src - osrc)); /* count does not include NUL */ 57 | } 58 | -------------------------------------------------------------------------------- /libopenbsd/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.13 2015/08/31 02:53:57 guenther Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "config.h" 20 | 21 | #include 22 | #include 23 | 24 | /* 25 | * Copy string src to buffer dst of size dsize. At most dsize-1 26 | * chars will be copied. Always NUL terminates (unless dsize == 0). 27 | * Returns strlen(src); if retval >= dsize, truncation occurred. 28 | */ 29 | size_t 30 | strlcpy(char *dst, const char *src, size_t dsize) 31 | { 32 | const char *osrc = src; 33 | size_t nleft = dsize; 34 | 35 | /* Copy as many bytes as will fit. */ 36 | if (nleft != 0) { 37 | while (--nleft != 0) { 38 | if ((*dst++ = *src++) == '\0') 39 | break; 40 | } 41 | } 42 | 43 | /* Not enough room in dst, add NUL and traverse rest of src. */ 44 | if (nleft == 0) { 45 | if (dsize != 0) 46 | *dst = '\0'; /* NUL-terminate dst */ 47 | while (*src++) 48 | ; 49 | } 50 | 51 | return(src - osrc - 1); /* count does not include NUL */ 52 | } 53 | -------------------------------------------------------------------------------- /libopenbsd/strtonum.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2004 Ted Unangst and Todd Miller 5 | * All rights reserved. 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 THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #include "config.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #define INVALID 1 27 | #define TOOSMALL 2 28 | #define TOOLARGE 3 29 | 30 | long long 31 | strtonum(const char *numstr, long long minval, long long maxval, 32 | const char **errstrp) 33 | { 34 | long long ll = 0; 35 | int error = 0; 36 | char *ep; 37 | struct errval { 38 | const char *errstr; 39 | int err; 40 | } ev[4] = { 41 | { NULL, 0 }, 42 | { "invalid", EINVAL }, 43 | { "too small", ERANGE }, 44 | { "too large", ERANGE }, 45 | }; 46 | 47 | ev[0].err = errno; 48 | errno = 0; 49 | if (minval > maxval) { 50 | error = INVALID; 51 | } else { 52 | ll = strtoll(numstr, &ep, 10); 53 | if (numstr == ep || *ep != '\0') 54 | error = INVALID; 55 | else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) 56 | error = TOOSMALL; 57 | else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) 58 | error = TOOLARGE; 59 | } 60 | if (errstrp != NULL) 61 | *errstrp = ev[error].errstr; 62 | errno = ev[error].err; 63 | if (error) 64 | ll = 0; 65 | 66 | return (ll); 67 | } 68 | -------------------------------------------------------------------------------- /libopenbsd/sys-readpassphrase.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2000, 2002 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * Sponsored in part by the Defense Advanced Research Projects 19 | * Agency (DARPA) and Air Force Research Laboratory, Air Force 20 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. 21 | */ 22 | 23 | /* OPENBSD ORIGINAL: include/readpassphrase.h */ 24 | 25 | #ifndef _READPASSPHRASE_H_ 26 | #define _READPASSPHRASE_H_ 27 | 28 | #ifndef HAVE_READPASSPHRASE 29 | 30 | #define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ 31 | #define RPP_ECHO_ON 0x01 /* Leave echo on. */ 32 | #define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ 33 | #define RPP_FORCELOWER 0x04 /* Force input to lower case. */ 34 | #define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ 35 | #define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ 36 | #define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ 37 | 38 | char * readpassphrase(const char *, char *, size_t, int); 39 | 40 | #endif /* HAVE_READPASSPHRASE */ 41 | 42 | #endif /* !_READPASSPHRASE_H_ */ 43 | -------------------------------------------------------------------------------- /libopenbsd/sys-time.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: time.h,v 1.37 2017/12/11 23:31:16 jca Exp $ */ 2 | /* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */ 3 | 4 | /* 5 | * Copyright (c) 1982, 1986, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * @(#)time.h 8.2 (Berkeley) 7/10/94 33 | */ 34 | 35 | #ifndef _SYS_TIME_H_ 36 | #define _SYS_TIME_H_ 37 | 38 | /* Operations on timespecs. */ 39 | #ifndef timespecisset 40 | #define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) 41 | #endif 42 | #ifndef timespeccmp 43 | #define timespeccmp(tsp, usp, cmp) \ 44 | (((tsp)->tv_sec == (usp)->tv_sec) ? \ 45 | ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ 46 | ((tsp)->tv_sec cmp (usp)->tv_sec)) 47 | #endif 48 | #ifndef timespecadd 49 | #define timespecadd(tsp, usp, vsp) \ 50 | do { \ 51 | (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ 52 | (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ 53 | if ((vsp)->tv_nsec >= 1000000000L) { \ 54 | (vsp)->tv_sec++; \ 55 | (vsp)->tv_nsec -= 1000000000L; \ 56 | } \ 57 | } while (0) 58 | #endif 59 | 60 | #endif /* !_SYS_TIME_H_ */ 61 | -------------------------------------------------------------------------------- /libopenbsd/sys-tree.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ 2 | /* 3 | * Copyright 2002 Niels Provos 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /* OPENBSD ORIGINAL: sys/sys/tree.h */ 28 | 29 | #ifdef NO_ATTRIBUTE_ON_RETURN_TYPE 30 | # define __attribute__(x) 31 | #endif 32 | 33 | #ifndef _SYS_TREE_H_ 34 | #define _SYS_TREE_H_ 35 | 36 | /* 37 | * This file defines data structures for different types of trees: 38 | * splay trees and red-black trees. 39 | * 40 | * A splay tree is a self-organizing data structure. Every operation 41 | * on the tree causes a splay to happen. The splay moves the requested 42 | * node to the root of the tree and partly rebalances it. 43 | * 44 | * This has the benefit that request locality causes faster lookups as 45 | * the requested nodes move to the top of the tree. On the other hand, 46 | * every lookup causes memory writes. 47 | * 48 | * The Balance Theorem bounds the total access time for m operations 49 | * and n inserts on an initially empty tree as O((m + n)lg n). The 50 | * amortized cost for a sequence of m accesses to a splay tree is O(lg n); 51 | * 52 | * A red-black tree is a binary search tree with the node color as an 53 | * extra attribute. It fulfills a set of conditions: 54 | * - every search path from the root to a leaf consists of the 55 | * same number of black nodes, 56 | * - each red node (except for the root) has a black parent, 57 | * - each leaf node is black. 58 | * 59 | * Every operation on a red-black tree is bounded as O(lg n). 60 | * The maximum height of a red-black tree is 2lg (n+1). 61 | */ 62 | 63 | #define SPLAY_HEAD(name, type) \ 64 | struct name { \ 65 | struct type *sph_root; /* root of the tree */ \ 66 | } 67 | 68 | #define SPLAY_INITIALIZER(root) \ 69 | { NULL } 70 | 71 | #define SPLAY_INIT(root) do { \ 72 | (root)->sph_root = NULL; \ 73 | } while (0) 74 | 75 | #define SPLAY_ENTRY(type) \ 76 | struct { \ 77 | struct type *spe_left; /* left element */ \ 78 | struct type *spe_right; /* right element */ \ 79 | } 80 | 81 | #define SPLAY_LEFT(elm, field) (elm)->field.spe_left 82 | #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right 83 | #define SPLAY_ROOT(head) (head)->sph_root 84 | #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) 85 | 86 | /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ 87 | #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ 88 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ 89 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 90 | (head)->sph_root = tmp; \ 91 | } while (0) 92 | 93 | #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ 94 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ 95 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 96 | (head)->sph_root = tmp; \ 97 | } while (0) 98 | 99 | #define SPLAY_LINKLEFT(head, tmp, field) do { \ 100 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 101 | tmp = (head)->sph_root; \ 102 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ 103 | } while (0) 104 | 105 | #define SPLAY_LINKRIGHT(head, tmp, field) do { \ 106 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 107 | tmp = (head)->sph_root; \ 108 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ 109 | } while (0) 110 | 111 | #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ 112 | SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ 113 | SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ 114 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ 115 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ 116 | } while (0) 117 | 118 | /* Generates prototypes and inline functions */ 119 | 120 | #define SPLAY_PROTOTYPE(name, type, field, cmp) \ 121 | void name##_SPLAY(struct name *, struct type *); \ 122 | void name##_SPLAY_MINMAX(struct name *, int); \ 123 | struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ 124 | struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ 125 | \ 126 | /* Finds the node with the same key as elm */ \ 127 | static __inline struct type * \ 128 | name##_SPLAY_FIND(struct name *head, struct type *elm) \ 129 | { \ 130 | if (SPLAY_EMPTY(head)) \ 131 | return(NULL); \ 132 | name##_SPLAY(head, elm); \ 133 | if ((cmp)(elm, (head)->sph_root) == 0) \ 134 | return (head->sph_root); \ 135 | return (NULL); \ 136 | } \ 137 | \ 138 | static __inline struct type * \ 139 | name##_SPLAY_NEXT(struct name *head, struct type *elm) \ 140 | { \ 141 | name##_SPLAY(head, elm); \ 142 | if (SPLAY_RIGHT(elm, field) != NULL) { \ 143 | elm = SPLAY_RIGHT(elm, field); \ 144 | while (SPLAY_LEFT(elm, field) != NULL) { \ 145 | elm = SPLAY_LEFT(elm, field); \ 146 | } \ 147 | } else \ 148 | elm = NULL; \ 149 | return (elm); \ 150 | } \ 151 | \ 152 | static __inline struct type * \ 153 | name##_SPLAY_MIN_MAX(struct name *head, int val) \ 154 | { \ 155 | name##_SPLAY_MINMAX(head, val); \ 156 | return (SPLAY_ROOT(head)); \ 157 | } 158 | 159 | /* Main splay operation. 160 | * Moves node close to the key of elm to top 161 | */ 162 | #define SPLAY_GENERATE(name, type, field, cmp) \ 163 | struct type * \ 164 | name##_SPLAY_INSERT(struct name *head, struct type *elm) \ 165 | { \ 166 | if (SPLAY_EMPTY(head)) { \ 167 | SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ 168 | } else { \ 169 | int __comp; \ 170 | name##_SPLAY(head, elm); \ 171 | __comp = (cmp)(elm, (head)->sph_root); \ 172 | if(__comp < 0) { \ 173 | SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ 174 | SPLAY_RIGHT(elm, field) = (head)->sph_root; \ 175 | SPLAY_LEFT((head)->sph_root, field) = NULL; \ 176 | } else if (__comp > 0) { \ 177 | SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ 178 | SPLAY_LEFT(elm, field) = (head)->sph_root; \ 179 | SPLAY_RIGHT((head)->sph_root, field) = NULL; \ 180 | } else \ 181 | return ((head)->sph_root); \ 182 | } \ 183 | (head)->sph_root = (elm); \ 184 | return (NULL); \ 185 | } \ 186 | \ 187 | struct type * \ 188 | name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ 189 | { \ 190 | struct type *__tmp; \ 191 | if (SPLAY_EMPTY(head)) \ 192 | return (NULL); \ 193 | name##_SPLAY(head, elm); \ 194 | if ((cmp)(elm, (head)->sph_root) == 0) { \ 195 | if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ 196 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ 197 | } else { \ 198 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 199 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ 200 | name##_SPLAY(head, elm); \ 201 | SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ 202 | } \ 203 | return (elm); \ 204 | } \ 205 | return (NULL); \ 206 | } \ 207 | \ 208 | void \ 209 | name##_SPLAY(struct name *head, struct type *elm) \ 210 | { \ 211 | struct type __node, *__left, *__right, *__tmp; \ 212 | int __comp; \ 213 | \ 214 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 215 | __left = __right = &__node; \ 216 | \ 217 | while ((__comp = (cmp)(elm, (head)->sph_root))) { \ 218 | if (__comp < 0) { \ 219 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 220 | if (__tmp == NULL) \ 221 | break; \ 222 | if ((cmp)(elm, __tmp) < 0){ \ 223 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 224 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 225 | break; \ 226 | } \ 227 | SPLAY_LINKLEFT(head, __right, field); \ 228 | } else if (__comp > 0) { \ 229 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 230 | if (__tmp == NULL) \ 231 | break; \ 232 | if ((cmp)(elm, __tmp) > 0){ \ 233 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 234 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 235 | break; \ 236 | } \ 237 | SPLAY_LINKRIGHT(head, __left, field); \ 238 | } \ 239 | } \ 240 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 241 | } \ 242 | \ 243 | /* Splay with either the minimum or the maximum element \ 244 | * Used to find minimum or maximum element in tree. \ 245 | */ \ 246 | void name##_SPLAY_MINMAX(struct name *head, int __comp) \ 247 | { \ 248 | struct type __node, *__left, *__right, *__tmp; \ 249 | \ 250 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 251 | __left = __right = &__node; \ 252 | \ 253 | while (1) { \ 254 | if (__comp < 0) { \ 255 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 256 | if (__tmp == NULL) \ 257 | break; \ 258 | if (__comp < 0){ \ 259 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 260 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 261 | break; \ 262 | } \ 263 | SPLAY_LINKLEFT(head, __right, field); \ 264 | } else if (__comp > 0) { \ 265 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 266 | if (__tmp == NULL) \ 267 | break; \ 268 | if (__comp > 0) { \ 269 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 270 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 271 | break; \ 272 | } \ 273 | SPLAY_LINKRIGHT(head, __left, field); \ 274 | } \ 275 | } \ 276 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 277 | } 278 | 279 | #define SPLAY_NEGINF -1 280 | #define SPLAY_INF 1 281 | 282 | #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) 283 | #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) 284 | #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) 285 | #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) 286 | #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ 287 | : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) 288 | #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ 289 | : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) 290 | 291 | #define SPLAY_FOREACH(x, name, head) \ 292 | for ((x) = SPLAY_MIN(name, head); \ 293 | (x) != NULL; \ 294 | (x) = SPLAY_NEXT(name, head, x)) 295 | 296 | /* Macros that define a red-black tree */ 297 | #define RB_HEAD(name, type) \ 298 | struct name { \ 299 | struct type *rbh_root; /* root of the tree */ \ 300 | } 301 | 302 | #define RB_INITIALIZER(root) \ 303 | { NULL } 304 | 305 | #define RB_INIT(root) do { \ 306 | (root)->rbh_root = NULL; \ 307 | } while (0) 308 | 309 | #define RB_BLACK 0 310 | #define RB_RED 1 311 | #define RB_ENTRY(type) \ 312 | struct { \ 313 | struct type *rbe_left; /* left element */ \ 314 | struct type *rbe_right; /* right element */ \ 315 | struct type *rbe_parent; /* parent element */ \ 316 | int rbe_color; /* node color */ \ 317 | } 318 | 319 | #define RB_LEFT(elm, field) (elm)->field.rbe_left 320 | #define RB_RIGHT(elm, field) (elm)->field.rbe_right 321 | #define RB_PARENT(elm, field) (elm)->field.rbe_parent 322 | #define RB_COLOR(elm, field) (elm)->field.rbe_color 323 | #define RB_ROOT(head) (head)->rbh_root 324 | #define RB_EMPTY(head) (RB_ROOT(head) == NULL) 325 | 326 | #define RB_SET(elm, parent, field) do { \ 327 | RB_PARENT(elm, field) = parent; \ 328 | RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ 329 | RB_COLOR(elm, field) = RB_RED; \ 330 | } while (0) 331 | 332 | #define RB_SET_BLACKRED(black, red, field) do { \ 333 | RB_COLOR(black, field) = RB_BLACK; \ 334 | RB_COLOR(red, field) = RB_RED; \ 335 | } while (0) 336 | 337 | #ifndef RB_AUGMENT 338 | #define RB_AUGMENT(x) do {} while (0) 339 | #endif 340 | 341 | #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ 342 | (tmp) = RB_RIGHT(elm, field); \ 343 | if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ 344 | RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ 345 | } \ 346 | RB_AUGMENT(elm); \ 347 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 348 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 349 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 350 | else \ 351 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 352 | } else \ 353 | (head)->rbh_root = (tmp); \ 354 | RB_LEFT(tmp, field) = (elm); \ 355 | RB_PARENT(elm, field) = (tmp); \ 356 | RB_AUGMENT(tmp); \ 357 | if ((RB_PARENT(tmp, field))) \ 358 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 359 | } while (0) 360 | 361 | #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ 362 | (tmp) = RB_LEFT(elm, field); \ 363 | if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ 364 | RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ 365 | } \ 366 | RB_AUGMENT(elm); \ 367 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 368 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 369 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 370 | else \ 371 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 372 | } else \ 373 | (head)->rbh_root = (tmp); \ 374 | RB_RIGHT(tmp, field) = (elm); \ 375 | RB_PARENT(elm, field) = (tmp); \ 376 | RB_AUGMENT(tmp); \ 377 | if ((RB_PARENT(tmp, field))) \ 378 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 379 | } while (0) 380 | 381 | /* Generates prototypes and inline functions */ 382 | #define RB_PROTOTYPE(name, type, field, cmp) \ 383 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) 384 | #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ 385 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) 386 | #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ 387 | attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ 388 | attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ 389 | attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ 390 | attr struct type *name##_RB_INSERT(struct name *, struct type *); \ 391 | attr struct type *name##_RB_FIND(struct name *, struct type *); \ 392 | attr struct type *name##_RB_NFIND(struct name *, struct type *); \ 393 | attr struct type *name##_RB_NEXT(struct type *); \ 394 | attr struct type *name##_RB_PREV(struct type *); \ 395 | attr struct type *name##_RB_MINMAX(struct name *, int); \ 396 | \ 397 | 398 | /* Main rb operation. 399 | * Moves node close to the key of elm to top 400 | */ 401 | #define RB_GENERATE(name, type, field, cmp) \ 402 | RB_GENERATE_INTERNAL(name, type, field, cmp,) 403 | #define RB_GENERATE_STATIC(name, type, field, cmp) \ 404 | RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) 405 | #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ 406 | attr void \ 407 | name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ 408 | { \ 409 | struct type *parent, *gparent, *tmp; \ 410 | while ((parent = RB_PARENT(elm, field)) && \ 411 | RB_COLOR(parent, field) == RB_RED) { \ 412 | gparent = RB_PARENT(parent, field); \ 413 | if (parent == RB_LEFT(gparent, field)) { \ 414 | tmp = RB_RIGHT(gparent, field); \ 415 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 416 | RB_COLOR(tmp, field) = RB_BLACK; \ 417 | RB_SET_BLACKRED(parent, gparent, field);\ 418 | elm = gparent; \ 419 | continue; \ 420 | } \ 421 | if (RB_RIGHT(parent, field) == elm) { \ 422 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 423 | tmp = parent; \ 424 | parent = elm; \ 425 | elm = tmp; \ 426 | } \ 427 | RB_SET_BLACKRED(parent, gparent, field); \ 428 | RB_ROTATE_RIGHT(head, gparent, tmp, field); \ 429 | } else { \ 430 | tmp = RB_LEFT(gparent, field); \ 431 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 432 | RB_COLOR(tmp, field) = RB_BLACK; \ 433 | RB_SET_BLACKRED(parent, gparent, field);\ 434 | elm = gparent; \ 435 | continue; \ 436 | } \ 437 | if (RB_LEFT(parent, field) == elm) { \ 438 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 439 | tmp = parent; \ 440 | parent = elm; \ 441 | elm = tmp; \ 442 | } \ 443 | RB_SET_BLACKRED(parent, gparent, field); \ 444 | RB_ROTATE_LEFT(head, gparent, tmp, field); \ 445 | } \ 446 | } \ 447 | RB_COLOR(head->rbh_root, field) = RB_BLACK; \ 448 | } \ 449 | \ 450 | attr void \ 451 | name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ 452 | { \ 453 | struct type *tmp; \ 454 | while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ 455 | elm != RB_ROOT(head)) { \ 456 | if (RB_LEFT(parent, field) == elm) { \ 457 | tmp = RB_RIGHT(parent, field); \ 458 | if (RB_COLOR(tmp, field) == RB_RED) { \ 459 | RB_SET_BLACKRED(tmp, parent, field); \ 460 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 461 | tmp = RB_RIGHT(parent, field); \ 462 | } \ 463 | if ((RB_LEFT(tmp, field) == NULL || \ 464 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 465 | (RB_RIGHT(tmp, field) == NULL || \ 466 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 467 | RB_COLOR(tmp, field) = RB_RED; \ 468 | elm = parent; \ 469 | parent = RB_PARENT(elm, field); \ 470 | } else { \ 471 | if (RB_RIGHT(tmp, field) == NULL || \ 472 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ 473 | struct type *oleft; \ 474 | if ((oleft = RB_LEFT(tmp, field)))\ 475 | RB_COLOR(oleft, field) = RB_BLACK;\ 476 | RB_COLOR(tmp, field) = RB_RED; \ 477 | RB_ROTATE_RIGHT(head, tmp, oleft, field);\ 478 | tmp = RB_RIGHT(parent, field); \ 479 | } \ 480 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 481 | RB_COLOR(parent, field) = RB_BLACK; \ 482 | if (RB_RIGHT(tmp, field)) \ 483 | RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ 484 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 485 | elm = RB_ROOT(head); \ 486 | break; \ 487 | } \ 488 | } else { \ 489 | tmp = RB_LEFT(parent, field); \ 490 | if (RB_COLOR(tmp, field) == RB_RED) { \ 491 | RB_SET_BLACKRED(tmp, parent, field); \ 492 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 493 | tmp = RB_LEFT(parent, field); \ 494 | } \ 495 | if ((RB_LEFT(tmp, field) == NULL || \ 496 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 497 | (RB_RIGHT(tmp, field) == NULL || \ 498 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 499 | RB_COLOR(tmp, field) = RB_RED; \ 500 | elm = parent; \ 501 | parent = RB_PARENT(elm, field); \ 502 | } else { \ 503 | if (RB_LEFT(tmp, field) == NULL || \ 504 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ 505 | struct type *oright; \ 506 | if ((oright = RB_RIGHT(tmp, field)))\ 507 | RB_COLOR(oright, field) = RB_BLACK;\ 508 | RB_COLOR(tmp, field) = RB_RED; \ 509 | RB_ROTATE_LEFT(head, tmp, oright, field);\ 510 | tmp = RB_LEFT(parent, field); \ 511 | } \ 512 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 513 | RB_COLOR(parent, field) = RB_BLACK; \ 514 | if (RB_LEFT(tmp, field)) \ 515 | RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ 516 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 517 | elm = RB_ROOT(head); \ 518 | break; \ 519 | } \ 520 | } \ 521 | } \ 522 | if (elm) \ 523 | RB_COLOR(elm, field) = RB_BLACK; \ 524 | } \ 525 | \ 526 | attr struct type * \ 527 | name##_RB_REMOVE(struct name *head, struct type *elm) \ 528 | { \ 529 | struct type *child, *parent, *old = elm; \ 530 | int color; \ 531 | if (RB_LEFT(elm, field) == NULL) \ 532 | child = RB_RIGHT(elm, field); \ 533 | else if (RB_RIGHT(elm, field) == NULL) \ 534 | child = RB_LEFT(elm, field); \ 535 | else { \ 536 | struct type *left; \ 537 | elm = RB_RIGHT(elm, field); \ 538 | while ((left = RB_LEFT(elm, field))) \ 539 | elm = left; \ 540 | child = RB_RIGHT(elm, field); \ 541 | parent = RB_PARENT(elm, field); \ 542 | color = RB_COLOR(elm, field); \ 543 | if (child) \ 544 | RB_PARENT(child, field) = parent; \ 545 | if (parent) { \ 546 | if (RB_LEFT(parent, field) == elm) \ 547 | RB_LEFT(parent, field) = child; \ 548 | else \ 549 | RB_RIGHT(parent, field) = child; \ 550 | RB_AUGMENT(parent); \ 551 | } else \ 552 | RB_ROOT(head) = child; \ 553 | if (RB_PARENT(elm, field) == old) \ 554 | parent = elm; \ 555 | (elm)->field = (old)->field; \ 556 | if (RB_PARENT(old, field)) { \ 557 | if (RB_LEFT(RB_PARENT(old, field), field) == old)\ 558 | RB_LEFT(RB_PARENT(old, field), field) = elm;\ 559 | else \ 560 | RB_RIGHT(RB_PARENT(old, field), field) = elm;\ 561 | RB_AUGMENT(RB_PARENT(old, field)); \ 562 | } else \ 563 | RB_ROOT(head) = elm; \ 564 | RB_PARENT(RB_LEFT(old, field), field) = elm; \ 565 | if (RB_RIGHT(old, field)) \ 566 | RB_PARENT(RB_RIGHT(old, field), field) = elm; \ 567 | if (parent) { \ 568 | left = parent; \ 569 | do { \ 570 | RB_AUGMENT(left); \ 571 | } while ((left = RB_PARENT(left, field))); \ 572 | } \ 573 | goto color; \ 574 | } \ 575 | parent = RB_PARENT(elm, field); \ 576 | color = RB_COLOR(elm, field); \ 577 | if (child) \ 578 | RB_PARENT(child, field) = parent; \ 579 | if (parent) { \ 580 | if (RB_LEFT(parent, field) == elm) \ 581 | RB_LEFT(parent, field) = child; \ 582 | else \ 583 | RB_RIGHT(parent, field) = child; \ 584 | RB_AUGMENT(parent); \ 585 | } else \ 586 | RB_ROOT(head) = child; \ 587 | color: \ 588 | if (color == RB_BLACK) \ 589 | name##_RB_REMOVE_COLOR(head, parent, child); \ 590 | return (old); \ 591 | } \ 592 | \ 593 | /* Inserts a node into the RB tree */ \ 594 | attr struct type * \ 595 | name##_RB_INSERT(struct name *head, struct type *elm) \ 596 | { \ 597 | struct type *tmp; \ 598 | struct type *parent = NULL; \ 599 | int comp = 0; \ 600 | tmp = RB_ROOT(head); \ 601 | while (tmp) { \ 602 | parent = tmp; \ 603 | comp = (cmp)(elm, parent); \ 604 | if (comp < 0) \ 605 | tmp = RB_LEFT(tmp, field); \ 606 | else if (comp > 0) \ 607 | tmp = RB_RIGHT(tmp, field); \ 608 | else \ 609 | return (tmp); \ 610 | } \ 611 | RB_SET(elm, parent, field); \ 612 | if (parent != NULL) { \ 613 | if (comp < 0) \ 614 | RB_LEFT(parent, field) = elm; \ 615 | else \ 616 | RB_RIGHT(parent, field) = elm; \ 617 | RB_AUGMENT(parent); \ 618 | } else \ 619 | RB_ROOT(head) = elm; \ 620 | name##_RB_INSERT_COLOR(head, elm); \ 621 | return (NULL); \ 622 | } \ 623 | \ 624 | /* Finds the node with the same key as elm */ \ 625 | attr struct type * \ 626 | name##_RB_FIND(struct name *head, struct type *elm) \ 627 | { \ 628 | struct type *tmp = RB_ROOT(head); \ 629 | int comp; \ 630 | while (tmp) { \ 631 | comp = cmp(elm, tmp); \ 632 | if (comp < 0) \ 633 | tmp = RB_LEFT(tmp, field); \ 634 | else if (comp > 0) \ 635 | tmp = RB_RIGHT(tmp, field); \ 636 | else \ 637 | return (tmp); \ 638 | } \ 639 | return (NULL); \ 640 | } \ 641 | \ 642 | /* Finds the first node greater than or equal to the search key */ \ 643 | attr struct type * \ 644 | name##_RB_NFIND(struct name *head, struct type *elm) \ 645 | { \ 646 | struct type *tmp = RB_ROOT(head); \ 647 | struct type *res = NULL; \ 648 | int comp; \ 649 | while (tmp) { \ 650 | comp = cmp(elm, tmp); \ 651 | if (comp < 0) { \ 652 | res = tmp; \ 653 | tmp = RB_LEFT(tmp, field); \ 654 | } \ 655 | else if (comp > 0) \ 656 | tmp = RB_RIGHT(tmp, field); \ 657 | else \ 658 | return (tmp); \ 659 | } \ 660 | return (res); \ 661 | } \ 662 | \ 663 | /* ARGSUSED */ \ 664 | attr struct type * \ 665 | name##_RB_NEXT(struct type *elm) \ 666 | { \ 667 | if (RB_RIGHT(elm, field)) { \ 668 | elm = RB_RIGHT(elm, field); \ 669 | while (RB_LEFT(elm, field)) \ 670 | elm = RB_LEFT(elm, field); \ 671 | } else { \ 672 | if (RB_PARENT(elm, field) && \ 673 | (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ 674 | elm = RB_PARENT(elm, field); \ 675 | else { \ 676 | while (RB_PARENT(elm, field) && \ 677 | (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ 678 | elm = RB_PARENT(elm, field); \ 679 | elm = RB_PARENT(elm, field); \ 680 | } \ 681 | } \ 682 | return (elm); \ 683 | } \ 684 | \ 685 | /* ARGSUSED */ \ 686 | attr struct type * \ 687 | name##_RB_PREV(struct type *elm) \ 688 | { \ 689 | if (RB_LEFT(elm, field)) { \ 690 | elm = RB_LEFT(elm, field); \ 691 | while (RB_RIGHT(elm, field)) \ 692 | elm = RB_RIGHT(elm, field); \ 693 | } else { \ 694 | if (RB_PARENT(elm, field) && \ 695 | (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ 696 | elm = RB_PARENT(elm, field); \ 697 | else { \ 698 | while (RB_PARENT(elm, field) && \ 699 | (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ 700 | elm = RB_PARENT(elm, field); \ 701 | elm = RB_PARENT(elm, field); \ 702 | } \ 703 | } \ 704 | return (elm); \ 705 | } \ 706 | \ 707 | attr struct type * \ 708 | name##_RB_MINMAX(struct name *head, int val) \ 709 | { \ 710 | struct type *tmp = RB_ROOT(head); \ 711 | struct type *parent = NULL; \ 712 | while (tmp) { \ 713 | parent = tmp; \ 714 | if (val < 0) \ 715 | tmp = RB_LEFT(tmp, field); \ 716 | else \ 717 | tmp = RB_RIGHT(tmp, field); \ 718 | } \ 719 | return (parent); \ 720 | } 721 | 722 | #define RB_NEGINF -1 723 | #define RB_INF 1 724 | 725 | #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) 726 | #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) 727 | #define RB_FIND(name, x, y) name##_RB_FIND(x, y) 728 | #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) 729 | #define RB_NEXT(name, x, y) name##_RB_NEXT(y) 730 | #define RB_PREV(name, x, y) name##_RB_PREV(y) 731 | #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) 732 | #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) 733 | 734 | #define RB_FOREACH(x, name, head) \ 735 | for ((x) = RB_MIN(name, head); \ 736 | (x) != NULL; \ 737 | (x) = name##_RB_NEXT(x)) 738 | 739 | #define RB_FOREACH_SAFE(x, name, head, y) \ 740 | for ((x) = RB_MIN(name, head); \ 741 | ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ 742 | (x) = (y)) 743 | 744 | #define RB_FOREACH_REVERSE(x, name, head) \ 745 | for ((x) = RB_MAX(name, head); \ 746 | (x) != NULL; \ 747 | (x) = name##_RB_PREV(x)) 748 | 749 | #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ 750 | for ((x) = RB_MAX(name, head); \ 751 | ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ 752 | (x) = (y)) 753 | 754 | #endif /* _SYS_TREE_H_ */ 755 | -------------------------------------------------------------------------------- /libopenbsd/verrc.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: verrc.c,v 1.3 2016/03/13 18:34:20 guenther Exp $ */ 2 | /*- 3 | * Copyright (c) 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "config.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "openbsd.h" 40 | 41 | void __dead 42 | verrc(int eval, int code, const char *fmt, va_list ap) 43 | { 44 | (void)fprintf(stderr, "%s: ", getprogname()); 45 | if (fmt != NULL) { 46 | (void)vfprintf(stderr, fmt, ap); 47 | (void)fprintf(stderr, ": "); 48 | } 49 | (void)fprintf(stderr, "%s\n", strerror(code)); 50 | exit(eval); 51 | } 52 | -------------------------------------------------------------------------------- /pam.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Nathan Holstein 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "config.h" 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #ifdef HAVE_READPASSPHRASE 27 | # include 28 | #else 29 | # include "sys-readpassphrase.h" 30 | #endif 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | #include "openbsd.h" 41 | #include "doas.h" 42 | 43 | #ifndef HOST_NAME_MAX 44 | #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX 45 | #endif 46 | 47 | #define PAM_SERVICE_NAME "doas" 48 | 49 | static pam_handle_t *pamh = NULL; 50 | static char doas_prompt[128]; 51 | static sig_atomic_t volatile caught_signal = 0; 52 | 53 | static void 54 | catchsig(int sig) 55 | { 56 | caught_signal = sig; 57 | } 58 | 59 | static char * 60 | pamprompt(const char *msg, int echo_on, int *ret) 61 | { 62 | const char *prompt; 63 | char *pass, buf[PAM_MAX_RESP_SIZE]; 64 | int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF); 65 | 66 | /* overwrite default prompt if it matches "Password:[ ]" */ 67 | if (strncmp(msg,"Password:", 9) == 0 && 68 | (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0'))) 69 | prompt = doas_prompt; 70 | else 71 | prompt = msg; 72 | 73 | pass = readpassphrase(prompt, buf, sizeof(buf), flags); 74 | if (!pass) 75 | *ret = PAM_CONV_ERR; 76 | else if (!(pass = strdup(pass))) 77 | *ret = PAM_BUF_ERR; 78 | else 79 | *ret = PAM_SUCCESS; 80 | 81 | explicit_bzero(buf, sizeof(buf)); 82 | return pass; 83 | } 84 | 85 | static int 86 | pamconv(int nmsgs, const struct pam_message **msgs, 87 | struct pam_response **rsps, __UNUSED void *ptr) 88 | { 89 | struct pam_response *rsp; 90 | int i, style; 91 | int ret; 92 | 93 | if (!(rsp = calloc(nmsgs, sizeof(struct pam_response)))) 94 | err(1, "could not allocate pam_response"); 95 | 96 | for (i = 0; i < nmsgs; i++) { 97 | switch (style = msgs[i]->msg_style) { 98 | case PAM_PROMPT_ECHO_OFF: 99 | case PAM_PROMPT_ECHO_ON: 100 | rsp[i].resp = pamprompt(msgs[i]->msg, style == PAM_PROMPT_ECHO_ON, &ret); 101 | if (ret != PAM_SUCCESS) 102 | goto fail; 103 | break; 104 | 105 | case PAM_ERROR_MSG: 106 | case PAM_TEXT_INFO: 107 | if (fprintf(stderr, "%s\n", msgs[i]->msg) < 0) 108 | goto fail; 109 | break; 110 | 111 | default: 112 | errx(1, "invalid PAM msg_style %d", style); 113 | } 114 | } 115 | 116 | *rsps = rsp; 117 | rsp = NULL; 118 | 119 | return PAM_SUCCESS; 120 | 121 | fail: 122 | /* overwrite and free response buffers */ 123 | for (i = 0; i < nmsgs; i++) { 124 | if (rsp[i].resp == NULL) 125 | continue; 126 | switch (msgs[i]->msg_style) { 127 | case PAM_PROMPT_ECHO_OFF: 128 | case PAM_PROMPT_ECHO_ON: 129 | explicit_bzero(rsp[i].resp, strlen(rsp[i].resp)); 130 | free(rsp[i].resp); 131 | } 132 | rsp[i].resp = NULL; 133 | } 134 | free(rsp); 135 | 136 | return PAM_CONV_ERR; 137 | } 138 | 139 | void 140 | pamcleanup(int ret, int sess, int cred) 141 | { 142 | if (sess) { 143 | ret = pam_close_session(pamh, 0); 144 | if (ret != PAM_SUCCESS) 145 | errx(1, "pam_close_session: %s", pam_strerror(pamh, ret)); 146 | } 147 | if (cred) { 148 | ret = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); 149 | if (ret != PAM_SUCCESS) 150 | warn("pam_setcred(?, PAM_DELETE_CRED | PAM_SILENT): %s", 151 | pam_strerror(pamh, ret)); 152 | } 153 | pam_end(pamh, ret); 154 | } 155 | 156 | void 157 | watchsession(pid_t child, int sess, int cred) 158 | { 159 | sigset_t sigs; 160 | struct sigaction act, oldact; 161 | int status = 1; 162 | 163 | /* block signals */ 164 | sigfillset(&sigs); 165 | if (sigprocmask(SIG_BLOCK, &sigs, NULL)) { 166 | warn("failed to block signals"); 167 | caught_signal = 1; 168 | goto close; 169 | } 170 | 171 | /* setup signal handler */ 172 | act.sa_handler = catchsig; 173 | sigemptyset(&act.sa_mask); 174 | act.sa_flags = 0; 175 | 176 | /* unblock SIGTERM and SIGALRM to catch them */ 177 | sigemptyset(&sigs); 178 | if (sigaddset(&sigs, SIGTERM) || 179 | sigaddset(&sigs, SIGALRM) || 180 | sigaddset(&sigs, SIGTSTP) || 181 | sigaction(SIGTERM, &act, &oldact) || 182 | sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { 183 | warn("failed to set signal handler"); 184 | caught_signal = 1; 185 | goto close; 186 | } 187 | 188 | /* wait for child to be terminated */ 189 | if (waitpid(child, &status, 0) != -1) { 190 | if (WIFSIGNALED(status)) { 191 | fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), 192 | WCOREDUMP(status) ? " (core dumped)" : ""); 193 | status = WTERMSIG(status) + 128; 194 | } else 195 | status = WEXITSTATUS(status); 196 | } 197 | else if (caught_signal) 198 | status = caught_signal + 128; 199 | else 200 | status = 1; 201 | 202 | close: 203 | if (caught_signal && child != (pid_t)-1) { 204 | fprintf(stderr, "\nSession terminated, killing shell\n"); 205 | kill(child, SIGTERM); 206 | } 207 | 208 | pamcleanup(PAM_SUCCESS, sess, cred); 209 | 210 | if (caught_signal) { 211 | if (child != (pid_t)-1) { 212 | /* kill child */ 213 | sleep(2); 214 | kill(child, SIGKILL); 215 | fprintf(stderr, " ...killed.\n"); 216 | } 217 | 218 | /* unblock cached signal and resend */ 219 | sigaction(SIGTERM, &oldact, NULL); 220 | if (caught_signal != SIGTERM) 221 | caught_signal = SIGKILL; 222 | kill(getpid(), caught_signal); 223 | } 224 | 225 | exit(status); 226 | } 227 | 228 | void 229 | pamauth(const char *user, const char *myname, int interactive, int nopass, int persist) 230 | { 231 | static const struct pam_conv conv = { 232 | .conv = pamconv, 233 | .appdata_ptr = NULL, 234 | }; 235 | const char *ttydev; 236 | pid_t child; 237 | int ret, sess = 0, cred = 0; 238 | 239 | #ifdef USE_TIMESTAMP 240 | int fd = -1; 241 | int valid = 0; 242 | #else 243 | (void) persist; 244 | #endif 245 | 246 | if (!user || !myname) 247 | errx(1, "Authentication failed"); 248 | 249 | ret = pam_start(PAM_SERVICE_NAME, myname, &conv, &pamh); 250 | if (ret != PAM_SUCCESS) 251 | errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed", 252 | PAM_SERVICE_NAME, myname); 253 | 254 | ret = pam_set_item(pamh, PAM_RUSER, myname); 255 | if (ret != PAM_SUCCESS) 256 | warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s", 257 | pam_strerror(pamh, ret), myname); 258 | 259 | if (isatty(0) && (ttydev = ttyname(0)) != NULL) { 260 | if (strncmp(ttydev, "/dev/", 5) == 0) 261 | ttydev += 5; 262 | 263 | ret = pam_set_item(pamh, PAM_TTY, ttydev); 264 | if (ret != PAM_SUCCESS) 265 | warn("pam_set_item(?, PAM_TTY, \"%s\"): %s", 266 | ttydev, pam_strerror(pamh, ret)); 267 | } 268 | 269 | 270 | #ifdef USE_TIMESTAMP 271 | if (persist) 272 | fd = timestamp_open(&valid, 5 * 60); 273 | if (fd != -1 && valid == 1) 274 | nopass = 1; 275 | #endif 276 | 277 | if (!nopass) { 278 | if (!interactive) 279 | errx(1, "Authentication required"); 280 | 281 | /* doas style prompt for pam */ 282 | char host[HOST_NAME_MAX + 1]; 283 | if (gethostname(host, sizeof(host))) 284 | snprintf(host, sizeof(host), "?"); 285 | snprintf(doas_prompt, sizeof(doas_prompt), 286 | "\rdoas (%.32s@%.32s) password: ", myname, host); 287 | 288 | /* authenticate */ 289 | ret = pam_authenticate(pamh, 0); 290 | if (ret != PAM_SUCCESS) { 291 | pamcleanup(ret, sess, cred); 292 | syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); 293 | errx(1, "Authentication failed"); 294 | } 295 | } 296 | 297 | 298 | ret = pam_acct_mgmt(pamh, 0); 299 | if (ret == PAM_NEW_AUTHTOK_REQD) 300 | ret = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 301 | 302 | /* account not vaild or changing the auth token failed */ 303 | if (ret != PAM_SUCCESS) { 304 | pamcleanup(ret, sess, cred); 305 | syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); 306 | errx(1, "Authentication failed"); 307 | } 308 | 309 | /* set PAM_USER to the user we want to be */ 310 | ret = pam_set_item(pamh, PAM_USER, user); 311 | if (ret != PAM_SUCCESS) 312 | warn("pam_set_item(?, PAM_USER, \"%s\"): %s", user, 313 | pam_strerror(pamh, ret)); 314 | 315 | ret = pam_setcred(pamh, PAM_REINITIALIZE_CRED); 316 | if (ret != PAM_SUCCESS) 317 | warn("pam_setcred(?, PAM_REINITIALIZE_CRED): %s", pam_strerror(pamh, ret)); 318 | else 319 | cred = 1; 320 | 321 | /* open session */ 322 | ret = pam_open_session(pamh, 0); 323 | if (ret != PAM_SUCCESS) 324 | errx(1, "pam_open_session: %s", pam_strerror(pamh, ret)); 325 | sess = 1; 326 | 327 | if ((child = fork()) == -1) { 328 | pamcleanup(PAM_ABORT, sess, cred); 329 | err(1, "fork"); 330 | } 331 | 332 | /* return as child */ 333 | if (child == 0) { 334 | #ifdef USE_TIMESTAMP 335 | if (fd != -1) 336 | close(fd); 337 | #endif 338 | return; 339 | } 340 | 341 | #ifdef USE_TIMESTAMP 342 | if (fd != -1) { 343 | timestamp_set(fd, 5 * 60); 344 | close(fd); 345 | } 346 | #endif 347 | watchsession(child, sess, cred); 348 | } 349 | -------------------------------------------------------------------------------- /parse.y: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: parse.y,v 1.10 2015/07/24 06:36:42 zhuk Exp $ */ 2 | /* 3 | * Copyright (c) 2015 Ted Unangst 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | %{ 19 | #include "config.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "openbsd.h" 32 | 33 | #include "doas.h" 34 | 35 | typedef struct { 36 | union { 37 | struct { 38 | int action; 39 | int options; 40 | const char *cmd; 41 | const char **cmdargs; 42 | const char **envlist; 43 | }; 44 | const char **strlist; 45 | const char *str; 46 | }; 47 | int lineno; 48 | int colno; 49 | } yystype; 50 | #define YYSTYPE yystype 51 | 52 | FILE *yyfp; 53 | 54 | struct rule **rules; 55 | size_t nrules; 56 | static size_t maxrules; 57 | 58 | int parse_errors = 0; 59 | 60 | static void yyerror(const char *, ...); 61 | static int yylex(void); 62 | 63 | static size_t 64 | arraylen(const char **arr) 65 | { 66 | size_t cnt = 0; 67 | 68 | while (*arr) { 69 | cnt++; 70 | arr++; 71 | } 72 | return cnt; 73 | } 74 | 75 | %} 76 | 77 | %token TPERMIT TDENY TAS TCMD TARGS 78 | %token TNOPASS TNOLOG TPERSIST TKEEPENV TSETENV 79 | %token TSTRING 80 | 81 | %% 82 | 83 | grammar: /* empty */ 84 | | grammar '\n' 85 | | grammar rule '\n' 86 | | error '\n' 87 | ; 88 | 89 | rule: action ident target cmd { 90 | struct rule *r; 91 | r = calloc(1, sizeof(*r)); 92 | if (!r) 93 | errx(1, "can't allocate rule"); 94 | r->action = $1.action; 95 | r->options = $1.options; 96 | r->envlist = $1.envlist; 97 | r->ident = $2.str; 98 | r->target = $3.str; 99 | r->cmd = $4.cmd; 100 | r->cmdargs = $4.cmdargs; 101 | if (nrules == maxrules) { 102 | if (maxrules == 0) 103 | maxrules = 32; 104 | rules = reallocarray(rules, maxrules, 105 | 2 * sizeof(*rules)); 106 | if (!rules) 107 | errx(1, "can't allocate rules"); 108 | maxrules *= 2; 109 | } 110 | rules[nrules++] = r; 111 | } ; 112 | 113 | action: TPERMIT options { 114 | $$.action = PERMIT; 115 | $$.options = $2.options; 116 | $$.envlist = $2.envlist; 117 | } | TDENY { 118 | $$.action = DENY; 119 | $$.options = 0; 120 | $$.envlist = NULL; 121 | } ; 122 | 123 | options: /* none */ { 124 | $$.options = 0; 125 | $$.envlist = NULL; 126 | } | options option { 127 | $$.options = $1.options | $2.options; 128 | $$.envlist = $1.envlist; 129 | if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) { 130 | yyerror("can't combine nopass and persist"); 131 | YYERROR; 132 | } 133 | if ($2.envlist) { 134 | if ($$.envlist) { 135 | yyerror("can't have two setenv sections"); 136 | YYERROR; 137 | } else 138 | $$.envlist = $2.envlist; 139 | } 140 | } ; 141 | option: TNOPASS { 142 | $$.options = NOPASS; 143 | $$.envlist = NULL; 144 | } | TNOLOG { 145 | $$.options = NOLOG; 146 | $$.envlist = NULL; 147 | } | TPERSIST { 148 | $$.options = PERSIST; 149 | $$.envlist = NULL; 150 | } | TKEEPENV { 151 | $$.options = KEEPENV; 152 | $$.envlist = NULL; 153 | } | TSETENV '{' strlist '}' { 154 | $$.options = 0; 155 | $$.envlist = $3.strlist; 156 | } ; 157 | 158 | strlist: /* empty */ { 159 | if (!($$.strlist = calloc(1, sizeof(char *)))) 160 | errx(1, "can't allocate strlist"); 161 | } | strlist TSTRING { 162 | int nstr = arraylen($1.strlist); 163 | if (!($$.strlist = reallocarray($1.strlist, nstr + 2, 164 | sizeof(char *)))) 165 | errx(1, "can't allocate strlist"); 166 | $$.strlist[nstr] = $2.str; 167 | $$.strlist[nstr + 1] = NULL; 168 | } ; 169 | 170 | 171 | ident: TSTRING { 172 | $$.str = $1.str; 173 | } ; 174 | 175 | target: /* optional */ { 176 | $$.str = NULL; 177 | } | TAS TSTRING { 178 | $$.str = $2.str; 179 | } ; 180 | 181 | cmd: /* optional */ { 182 | $$.cmd = NULL; 183 | $$.cmdargs = NULL; 184 | } | TCMD TSTRING args { 185 | $$.cmd = $2.str; 186 | $$.cmdargs = $3.cmdargs; 187 | } ; 188 | 189 | args: /* empty */ { 190 | $$.cmdargs = NULL; 191 | } | TARGS strlist { 192 | $$.cmdargs = $2.strlist; 193 | } ; 194 | 195 | %% 196 | 197 | void 198 | yyerror(const char *fmt, ...) 199 | { 200 | va_list va; 201 | 202 | fprintf(stderr, "doas: "); 203 | va_start(va, fmt); 204 | vfprintf(stderr, fmt, va); 205 | va_end(va); 206 | fprintf(stderr, " at line %d\n", yylval.lineno + 1); 207 | parse_errors++; 208 | } 209 | 210 | static struct keyword { 211 | const char *word; 212 | int token; 213 | } keywords[] = { 214 | { "deny", TDENY }, 215 | { "permit", TPERMIT }, 216 | { "as", TAS }, 217 | { "cmd", TCMD }, 218 | { "args", TARGS }, 219 | { "nopass", TNOPASS }, 220 | { "nolog", TNOLOG }, 221 | { "persist", TPERSIST }, 222 | { "keepenv", TKEEPENV }, 223 | { "setenv", TSETENV }, 224 | }; 225 | 226 | int 227 | yylex(void) 228 | { 229 | char buf[1024], *ebuf, *p, *str; 230 | int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; 231 | size_t i; 232 | 233 | p = buf; 234 | ebuf = buf + sizeof(buf); 235 | 236 | repeat: 237 | /* skip whitespace first */ 238 | for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp)) 239 | yylval.colno++; 240 | 241 | /* check for special one-character constructions */ 242 | switch (c) { 243 | case '\n': 244 | yylval.colno = 0; 245 | yylval.lineno++; 246 | /* FALLTHROUGH */ 247 | case '{': 248 | case '}': 249 | return c; 250 | case '#': 251 | /* skip comments; NUL is allowed; no continuation */ 252 | while ((c = getc(yyfp)) != '\n') 253 | if (c == EOF) 254 | goto eof; 255 | yylval.colno = 0; 256 | yylval.lineno++; 257 | return c; 258 | case EOF: 259 | goto eof; 260 | } 261 | 262 | /* parsing next word */ 263 | for (;; c = getc(yyfp), yylval.colno++) { 264 | switch (c) { 265 | case '\0': 266 | yyerror("unallowed character NUL in column %d", 267 | yylval.colno + 1); 268 | escape = 0; 269 | continue; 270 | case '\\': 271 | escape = !escape; 272 | if (escape) 273 | continue; 274 | break; 275 | case '\n': 276 | if (quotes) 277 | yyerror("unterminated quotes in column %d", 278 | qpos + 1); 279 | if (escape) { 280 | nonkw = 1; 281 | escape = 0; 282 | yylval.colno = 0; 283 | yylval.lineno++; 284 | continue; 285 | } 286 | goto eow; 287 | case EOF: 288 | if (escape) 289 | yyerror("unterminated escape in column %d", 290 | yylval.colno); 291 | if (quotes) 292 | yyerror("unterminated quotes in column %d", 293 | qpos + 1); 294 | goto eow; 295 | /* FALLTHROUGH */ 296 | case '{': 297 | case '}': 298 | case '#': 299 | case ' ': 300 | case '\t': 301 | if (!escape && !quotes) 302 | goto eow; 303 | break; 304 | case '"': 305 | if (!escape) { 306 | quotes = !quotes; 307 | if (quotes) { 308 | nonkw = 1; 309 | qpos = yylval.colno; 310 | } 311 | continue; 312 | } 313 | } 314 | *p++ = c; 315 | if (p == ebuf) { 316 | yyerror("too long line"); 317 | p = buf; 318 | } 319 | escape = 0; 320 | } 321 | 322 | eow: 323 | *p = 0; 324 | if (c != EOF) 325 | ungetc(c, yyfp); 326 | if (p == buf) { 327 | /* 328 | * There could be a number of reasons for empty buffer, 329 | * and we handle all of them here, to avoid cluttering 330 | * the main loop. 331 | */ 332 | if (c == EOF) 333 | goto eof; 334 | else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */ 335 | goto repeat; 336 | } 337 | if (!nonkw) { 338 | for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { 339 | if (strcmp(buf, keywords[i].word) == 0) 340 | return keywords[i].token; 341 | } 342 | } 343 | if ((str = strdup(buf)) == NULL) 344 | err(1, "%s", __func__); 345 | yylval.str = str; 346 | return TSTRING; 347 | 348 | eof: 349 | if (ferror(yyfp)) 350 | yyerror("input error reading config"); 351 | return 0; 352 | } 353 | -------------------------------------------------------------------------------- /shadow.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Duncan Overbruck 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "config.h" 18 | 19 | #if HAVE_CRYPT_H 20 | # include 21 | #endif 22 | #include 23 | #include 24 | #include 25 | #include 26 | #ifdef HAVE_READPASSPHRASE 27 | # include 28 | #else 29 | # include "sys-readpassphrase.h" 30 | #endif 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "openbsd.h" 38 | #include "doas.h" 39 | 40 | #ifndef HOST_NAME_MAX 41 | #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX 42 | #endif 43 | 44 | void 45 | shadowauth(const char *myname, int persist) 46 | { 47 | const char *hash; 48 | char *encrypted; 49 | struct passwd *pw; 50 | char *challenge, *response, rbuf[1024], cbuf[128]; 51 | 52 | #ifdef USE_TIMESTAMP 53 | int fd = -1; 54 | int valid = 0; 55 | 56 | if (persist) 57 | fd = timestamp_open(&valid, 5 * 60); 58 | if (fd != -1 && valid == 1) 59 | goto good; 60 | #else 61 | (void) persist; 62 | #endif 63 | 64 | if ((pw = getpwnam(myname)) == NULL) 65 | err(1, "getpwnam"); 66 | 67 | hash = pw->pw_passwd; 68 | if (hash[0] == 'x' && hash[1] == '\0') { 69 | struct spwd *sp; 70 | if ((sp = getspnam(myname)) == NULL) 71 | errx(1, "Authentication failed"); 72 | hash = sp->sp_pwdp; 73 | } else if (hash[0] != '*') { 74 | errx(1, "Authentication failed"); 75 | } 76 | 77 | char host[HOST_NAME_MAX + 1]; 78 | if (gethostname(host, sizeof(host))) 79 | snprintf(host, sizeof(host), "?"); 80 | snprintf(cbuf, sizeof(cbuf), 81 | "\rdoas (%.32s@%.32s) password: ", myname, host); 82 | challenge = cbuf; 83 | 84 | response = readpassphrase(challenge, rbuf, sizeof(rbuf), RPP_REQUIRE_TTY); 85 | if (response == NULL && errno == ENOTTY) { 86 | syslog(LOG_AUTHPRIV | LOG_NOTICE, 87 | "tty required for %s", myname); 88 | errx(1, "a tty is required"); 89 | } 90 | if (response == NULL) 91 | err(1, "readpassphrase"); 92 | if ((encrypted = crypt(response, hash)) == NULL) { 93 | explicit_bzero(rbuf, sizeof(rbuf)); 94 | errx(1, "Authentication failed"); 95 | } 96 | explicit_bzero(rbuf, sizeof(rbuf)); 97 | if (strcmp(encrypted, hash) != 0) { 98 | syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); 99 | errx(1, "Authentication failed"); 100 | } 101 | 102 | #ifdef USE_TIMESTAMP 103 | good: 104 | if (fd != -1) { 105 | timestamp_set(fd, 5 * 60); 106 | close(fd); 107 | } 108 | #endif 109 | } 110 | -------------------------------------------------------------------------------- /timestamp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Duncan Overbruck 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "config.h" 18 | 19 | /* 20 | * 1) Timestamp files and directories 21 | * 22 | * Timestamp files MUST NOT be accessible to users other than root, 23 | * this includes the name, metadata and the content of timestamp files 24 | * and directories. 25 | * 26 | * Symlinks can be used to create, manipulate or delete wrong files 27 | * and directories. The Implementation MUST reject any symlinks for 28 | * timestamp files or directories. 29 | * 30 | * To avoid race conditions the implementation MUST use the same 31 | * file descriptor for permission checks and do read or write 32 | * write operations after the permission checks. 33 | * 34 | * The timestamp files MUST be opened with openat(2) using the 35 | * timestamp directory file descriptor. Permissions of the directory 36 | * MUST be checked before opening the timestamp file descriptor. 37 | * 38 | * 2) Clock sources for timestamps 39 | * 40 | * Timestamp files MUST NOT rely on only one clock source, using the 41 | * wall clock would allow to reset the clock to an earlier point in 42 | * time to reuse a timestamp. 43 | * 44 | * The timestamp MUST consist of multiple clocks and MUST reject the 45 | * timestamp if there is a change to any clock because there is no way 46 | * to differentiate between malicious and legitimate clock changes. 47 | * 48 | * 3) Timestamp lifetime 49 | * 50 | * The implementation MUST NOT use the user controlled stdin, stdout 51 | * and stderr file descriptors to determine the controlling terminal. 52 | * On linux the /proc/$pid/stat file MUST be used to get the terminal 53 | * number. 54 | * 55 | * There is no reliable way to determine the lifetime of a tty/pty. 56 | * The start time of the session leader MUST be used as part of the 57 | * timestamp to determine if the tty is still the same. 58 | * If the start time of the session leader changed the timestamp MUST 59 | * be rejected. 60 | * 61 | */ 62 | 63 | #include 64 | #include 65 | #include 66 | 67 | #if !defined(timespecisset) || \ 68 | !defined(timespeccmp) || \ 69 | !defined(timespecadd) 70 | # include "sys-time.h" 71 | #endif 72 | 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | 85 | #include "openbsd.h" 86 | #include "doas.h" 87 | 88 | #ifndef TIMESTAMP_DIR 89 | # define TIMESTAMP_DIR "/run/doas" 90 | #endif 91 | 92 | #if defined(TIMESTAMP_TMPFS) && defined(__linux__) 93 | # ifndef TMPFS_MAGIC 94 | # define TMPFS_MAGIC 0x01021994 95 | # endif 96 | #endif 97 | 98 | #ifdef __linux__ 99 | /* Use tty_nr from /proc/self/stat instead of using 100 | * ttyname(3), stdin, stdout and stderr are user 101 | * controllable and would allow to reuse timestamps 102 | * from another writable terminal. 103 | * See https://www.sudo.ws/alerts/tty_tickets.html 104 | */ 105 | static int 106 | proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) 107 | { 108 | char path[128]; 109 | char buf[1024]; 110 | char *p, *saveptr, *ep; 111 | const char *errstr; 112 | int fd, n; 113 | 114 | p = buf; 115 | 116 | n = snprintf(path, sizeof path, "/proc/%d/stat", pid); 117 | if (n < 0 || n >= (int)sizeof path) 118 | return -1; 119 | 120 | if ((fd = open(path, O_RDONLY|O_NOFOLLOW)) == -1) { 121 | warn("failed to open: %s", path); 122 | return -1; 123 | } 124 | 125 | while ((n = read(fd, p, buf + (sizeof buf - 1) - p)) != 0) { 126 | if (n == -1) { 127 | if (errno == EAGAIN || errno == EINTR) 128 | continue; 129 | warn("read: %s", path); 130 | close(fd); 131 | return -1; 132 | } 133 | p += n; 134 | if (p >= buf + (sizeof buf - 1)) 135 | break; 136 | } 137 | close(fd); 138 | 139 | /* error if it contains NULL bytes */ 140 | if (n != 0 || memchr(buf, '\0', p - buf - 1) != NULL) { 141 | warn("NUL in: %s", path); 142 | return -1; 143 | } 144 | 145 | *p = '\0'; 146 | 147 | /* Get the 7th field, 5 fields after the last ')', 148 | * (2th field) because the 5th field 'comm' can include 149 | * spaces and closing paranthesis too. 150 | * See https://www.sudo.ws/alerts/linux_tty.html 151 | */ 152 | if ((p = strrchr(buf, ')')) == NULL) 153 | return -1; 154 | 155 | n = 2; 156 | for ((p = strtok_r(p, " ", &saveptr)); p; 157 | (p = strtok_r(NULL, " ", &saveptr))) { 158 | switch (n++) { 159 | case 7: 160 | *ttynr = strtonum(p, INT_MIN, INT_MAX, &errstr); 161 | if (errstr) 162 | return -1; 163 | break; 164 | case 22: 165 | errno = 0; 166 | *starttime = strtoull(p, &ep, 10); 167 | if (p == ep || 168 | (errno == ERANGE && *starttime == ULLONG_MAX)) 169 | return -1; 170 | return 0; 171 | } 172 | } 173 | 174 | return -1; 175 | } 176 | #else 177 | #error "proc_info not implemented" 178 | #endif 179 | 180 | static int 181 | timestamp_path(char *buf, size_t len) 182 | { 183 | pid_t ppid, sid; 184 | unsigned long long starttime; 185 | int n, ttynr; 186 | 187 | ppid = getppid(); 188 | if ((sid = getsid(0)) == -1) 189 | return -1; 190 | if (proc_info(ppid, &ttynr, &starttime) == -1) 191 | return -1; 192 | n = snprintf(buf, len, TIMESTAMP_DIR "/%d-%d-%d-%llu-%d", 193 | ppid, sid, ttynr, starttime, getuid()); 194 | if (n < 0 || n >= (int)len) 195 | return -1; 196 | return 0; 197 | } 198 | 199 | int 200 | timestamp_set(int fd, int secs) 201 | { 202 | struct timespec ts[2], timeout = { .tv_sec = secs, .tv_nsec = 0 }; 203 | 204 | if (clock_gettime(CLOCK_BOOTTIME, &ts[0]) == -1 || 205 | clock_gettime(CLOCK_REALTIME, &ts[1]) == -1) 206 | return -1; 207 | 208 | timespecadd(&ts[0], &timeout, &ts[0]); 209 | timespecadd(&ts[1], &timeout, &ts[1]); 210 | return futimens(fd, ts); 211 | } 212 | 213 | /* 214 | * Returns 1 if the timestamp is valid, 0 if its invalid 215 | */ 216 | static int 217 | timestamp_check(int fd, int secs) 218 | { 219 | struct timespec ts[2], timeout = { .tv_sec = secs, .tv_nsec = 0 }; 220 | struct stat st; 221 | 222 | if (fstat(fd, &st) == -1) 223 | err(1, "fstat"); 224 | if (st.st_uid != 0 || st.st_gid != getgid() || st.st_mode != (S_IFREG | 0000)) 225 | errx(1, "timestamp uid, gid or mode wrong"); 226 | 227 | /* this timestamp was created but never set, invalid but no error */ 228 | if (!timespecisset(&st.st_atim) || !timespecisset(&st.st_mtim)) 229 | return 0; 230 | 231 | if (clock_gettime(CLOCK_BOOTTIME, &ts[0]) == -1 || 232 | clock_gettime(CLOCK_REALTIME, &ts[1]) == -1) { 233 | warn("clock_gettime"); 234 | return 0; 235 | } 236 | 237 | /* check if timestamp is too old */ 238 | if (timespeccmp(&st.st_atim, &ts[0], <) || 239 | timespeccmp(&st.st_mtim, &ts[1], <)) 240 | return 0; 241 | 242 | /* check if timestamp is too far in the future */ 243 | timespecadd(&ts[0], &timeout, &ts[0]); 244 | timespecadd(&ts[1], &timeout, &ts[1]); 245 | if (timespeccmp(&st.st_atim, &ts[0], >) || 246 | timespeccmp(&st.st_mtim, &ts[1], >)) { 247 | warnx("timestamp too far in the future"); 248 | return 0; 249 | } 250 | 251 | return 1; 252 | } 253 | 254 | int 255 | timestamp_open(int *valid, int secs) 256 | { 257 | struct timespec ts[2] = {0}; 258 | struct stat st; 259 | int fd; 260 | char path[256]; 261 | int serrno = 0; 262 | 263 | *valid = 0; 264 | 265 | if (stat(TIMESTAMP_DIR, &st) == -1) { 266 | if (errno != ENOENT) 267 | return -1; 268 | if (mkdir(TIMESTAMP_DIR, 0700) == -1) 269 | return -1; 270 | } else if (st.st_uid != 0 || st.st_mode != (S_IFDIR | 0700)) { 271 | return -1; 272 | } 273 | 274 | if (timestamp_path(path, sizeof path) == -1) 275 | return -1; 276 | 277 | fd = open(path, O_RDONLY|O_NOFOLLOW); 278 | if (fd == -1) { 279 | char tmp[256]; 280 | int n; 281 | 282 | if (errno != ENOENT) 283 | err(1, "open: %s", path); 284 | 285 | n = snprintf(tmp, sizeof tmp, TIMESTAMP_DIR "/.tmp-%d", getpid()); 286 | if (n < 0 || n >= (int)sizeof tmp) 287 | return -1; 288 | 289 | fd = open(tmp, O_RDONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0000); 290 | if (fd == -1) 291 | return -1; 292 | if (futimens(fd, ts) == -1 || rename(tmp, path) == -1) { 293 | serrno = errno; 294 | close(fd); 295 | unlink(tmp); 296 | errno = serrno; 297 | return -1; 298 | } 299 | } else { 300 | *valid = timestamp_check(fd, secs); 301 | } 302 | return fd; 303 | } 304 | 305 | int 306 | timestamp_clear() 307 | { 308 | char path[256]; 309 | 310 | if (timestamp_path(path, sizeof path) == -1) 311 | return -1; 312 | if (unlink(path) == -1 && errno != ENOENT) 313 | return -1; 314 | return 0; 315 | } 316 | --------------------------------------------------------------------------------