commit 36e80b7
Emilia Smólska
·
2026-07-25 23:59:11 +0000 UTC
parent 36e80b7
import
5 files changed,
+385,
-0
A
Makefile
+38,
-0
1@@ -0,0 +1,38 @@
2+.POSIX:
3+.SUFFIXES:
4+
5+CC= cc
6+CFLAGS= -g -Wall -Wextra -Wpedantic
7+
8+CPP= cpp
9+SED= sed
10+
11+PREFIX= /usr/local
12+SBINDIR= ${DESTDIR}${PREFIX}/sbin
13+
14+PROG= verily
15+OBJS= verily.o
16+
17+MAN= verily.8
18+MANIN= verily.8.in
19+
20+all: ${PROG} ${MAN}
21+
22+${PROG}: ${OBJS}
23+ ${CC} ${LDFLAGS} -o $@ ${OBJS}
24+
25+${OBJS}: arg.h config
26+
27+.SUFFIXES: .c .o
28+.c.o:
29+ ${CC} ${CFLAGS} -c -o $@ $<
30+
31+${MAN}: ${MANIN} config
32+ ${CPP} ${CPPFLAGS} ${MANIN} | ${SED} -e '/^#/d' -e '/^$$/d' >$@
33+
34+clean:
35+ rm -f *.core *.o ${PROG} ${MAN}
36+
37+install:
38+ cp ${PROG} ${SBINDIR}/${PROG}
39+ chmod u+s ${SBINDIR}/${PROG}
A
arg.h
+33,
-0
1@@ -0,0 +1,33 @@
2+#define nil ((void *)0)
3+
4+extern char *argv0;
5+
6+#define ARGBEGIN for((argv0 || (argv0 = argv[0])), argc--, argv++;\
7+ argv[0] && argv[0][0] == '-' && argv[0][1];\
8+ argc--, argv++){\
9+ char *_args;\
10+ char *_argt;\
11+ char _argc;\
12+ _args = &argv[0][1];\
13+ if(_args[0] == '-' && _args[1] == '\0'){\
14+ argc--, argv++;\
15+ break;\
16+ }\
17+ _argc = '\0';\
18+ while(_args[0] && (_argc = *_args++))\
19+ switch(_argc)
20+
21+#define ARGEND (void)_args, _argt, _argc;\
22+ }\
23+ (void)argc, argv;
24+
25+#define ARGF() (_argt = _args, _args = "",\
26+ (_argt[0] ? _argt :\
27+ argv[1] ? (argc--, *++argv) : nil))
28+
29+#define EARGF(x) (_argt = _args, _args = "",\
30+ (_argt[0] ? _argt :\
31+ argv[1] ? (argc--, *++argv) :\
32+ ((x), abort(), nil)))
33+
34+#define ARGC() _argc
A
config
+25,
-0
1@@ -0,0 +1,25 @@
2+/* file to check for write access */
3+#ifndef CHECKFILE
4+#define CHECKFILE /etc/verily
5+#endif
6+
7+/* group to check for membership */
8+#ifndef CHECKGRP
9+#define CHECKGRP wheel
10+#endif
11+
12+/* support for -r flag */
13+/* #define WITH_CHROOT */
14+
15+/* pledge system call present */
16+#ifdef __OpenBSD__
17+#define HAVE_PLEDGE
18+#endif
19+
20+/* setclasscontext library function present */
21+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
22+#define HAVE_SETCLASSCONTEXT
23+#endif
24+
25+/* for compilers without attribute support */
26+/* #define __attribute(_) */
+107,
-0
1@@ -0,0 +1,107 @@
2+#include "config"
3+.\" -*- coding: utf-8; -*- "
4+.Dd $Mdocdate$
5+.Dt verily 8
6+.Os
7+.Sh NAME
8+.Nm verily
9+.Nd run commands as different class, group, or user
10+.Sh SYNOPSIS
11+.Nm
12+#ifdef HAVE_SETCLASSCONTEXT
13+.Op Fl c
14+.Op Ar class
15+#endif
16+.Op Fl G Ar gid
17+.Op Fl g Ar group
18+#ifdef CONFIG_CHROOT
19+.Op Fl r Ar dir
20+#endif
21+.Op Fl U Ar uid
22+.Op Fl u Ar user
23+.Op Ar cmd
24+.Op Ar args ...
25+.Sh DESCRIPTION
26+The
27+.Nm
28+utility allows a user to run a command
29+.Po Ev $SHELL Fl i
30+if no command is specified
31+.Pc
32+with a different
33+#ifdef HAVE_SETCLASSCONTEXT
34+login class,
35+#endif
36+group id,
37+#ifdef CONFIG_CHROOT
38+root directory,
39+#endif
40+or user id, provided that the user is a member of the CHECKGRP group and has
41+write permission on
42+.Pa CHECKFILE .
43+The options are as follows:
44+.Bl -tag -width 8n
45+#ifdef HAVE_SETCLASSCONTEXT
46+.It Fl c Ar class
47+Specify a login class.
48+#endif
49+.It Fl G Ar gid
50+Specify a group id.
51+.It Fl g Ar group
52+Specify a group name.
53+#ifdef CONFIG_CHROOT
54+.It Fl r Ar dir
55+Specify a new root directory to
56+.Xr chroot 2
57+into.
58+#endif
59+.It Fl U Ar uid
60+Specify a user id. If the
61+.Fl s
62+option is not supplied in an invocation, the
63+.Nm
64+utility defaults to user id 0.
65+.It Fl u Ar user
66+Specify a user name.
67+.El
68+.Sh ENVIRONMENT
69+.Bl -tag -width Ds
70+.It Ev SHELL
71+Used as the default command
72+.Pq with the Fl i No flag
73+when invoked without arguments.
74+.El
75+.Sh FILES
76+.Bl -tag -width Ds
77+.It Pa CHECKFILE
78+The user must have write permission on this file in order to invoke the
79+.Nm
80+utility.
81+.El
82+.Sh EXIT STATUS
83+The
84+.Nm
85+utility exits 0 on success, and >0 if an error occurs or if the user is not
86+authorized.
87+.Sh SEE ALSO
88+#ifdef CONFIG_CHROOT
89+.Xr chroot 1 ,
90+#endif
91+.Xr doas 1 ,
92+#ifdef HAVE_SETCLASSCONTEXT
93+.Xr login 1 ,
94+#endif
95+.Xr su 1 ,
96+.Xr sudo 1 ,
97+.Xr really 8 .
98+.Sh AUTHORS
99+.An Emilia Smólska Aq Mt emilia@hopeserv.net
100+.Sh SECURITY CONSIDERATIONS
101+Installing
102+.Nm
103+setuid is unlikely to compromise the security of the system. It is intended to
104+serve as a convenient safeguard for administrator accounts that are equivalent
105+in power to root, and not as a comprehensive user-switching tool like
106+.Xr doas 1 .
107+.Sh MAKE ME A SANDWICH
108+.Lk "https://xkcd.com/149"
A
verily.c
+182,
-0
1@@ -0,0 +1,182 @@
2+#include <errno.h>
3+#include <grp.h>
4+#include <limits.h>
5+#include <pwd.h>
6+#include <stdarg.h>
7+#include <stdbool.h>
8+#include <stdnoreturn.h>
9+#include <stdio.h>
10+#include <stdlib.h>
11+#include <string.h>
12+#include <unistd.h>
13+
14+#include "arg.h"
15+#include "config"
16+
17+#ifdef HAVE_SETCLASSCONTEXT
18+#include <login_cap.h>
19+#endif
20+
21+#define nelem(x) (long)(sizeof(x) / sizeof((x)[0]))
22+
23+#define STRINGIFY1(x) #x
24+#define STRINGIFY(x) STRINGIFY1(x)
25+#define CHECKFILESTR STRINGIFY(CHECKFILE)
26+#define CHECKGRPSTR STRINGIFY(CHECKGRP)
27+
28+char *argv0;
29+
30+__attribute((format(printf, 1, 2)))
31+static noreturn void die(char *, ...);
32+static noreturn void usage(void);
33+
34+int
35+main(int argc, char **argv)
36+{
37+ gid_t groups[NGROUPS_MAX];
38+ char *shcmd[3] = {nil, "-i", nil};
39+ char **cmd;
40+ char *arg;
41+ struct group *wheel;
42+ int ngroups;
43+ bool iswheel;
44+
45+ argv0 = argv[0];
46+
47+#if !defined(WITH_CHROOT) && defined(HAVE_PLEDGE)
48+ if(pledge("stdio rpath getpw exec id", nil) < 0)
49+ die("pledge: %s", strerror(errno));
50+#endif
51+
52+ wheel = getgrnam(CHECKGRPSTR);
53+ if(!wheel)
54+ die(CHECKGRPSTR ": No such group");
55+ ngroups = getgroups(nelem(groups), groups);
56+ if(ngroups < 0)
57+ die("getgroups: %s", strerror(errno));
58+ iswheel = false;
59+ for(int i = 0; i < nelem(groups); i++)
60+ if(groups[i] == wheel->gr_gid)
61+ iswheel = true;
62+ if(!iswheel)
63+ die("Not in the " CHECKGRPSTR " group");
64+
65+ if(access(CHECKFILESTR, W_OK) < 0)
66+ die(CHECKFILESTR ": %s", strerror(errno));
67+
68+ if(argc <= 1 || argv[1][0] != '-')
69+ if(seteuid(0) < 0)
70+ die("seteuid 0: %s", strerror(errno));
71+
72+ ARGBEGIN{
73+#ifdef HAVE_SETCLASSCONTEXT
74+ case 'c': {
75+ arg = EARGF(usage());
76+ if(setclasscontext(arg, LOGIN_SETALL) < 0)
77+ die("setclasscontext %s: %s", arg, strerror(errno));
78+ break;
79+ }
80+#endif
81+ case 'G': {
82+ char *end;
83+ gid_t gid;
84+
85+ arg = EARGF(usage());
86+ gid = strtol(arg, &end, 10);
87+ if(arg[0] == '\0' || end[0] != '\0')
88+ usage();
89+ if(setegid(gid) < 0)
90+ die("setegid %u: %s", gid, strerror(errno));
91+ break;
92+ }
93+ case 'g': {
94+ struct group *group;
95+
96+ arg = EARGF(usage());
97+ group = getgrnam(arg);
98+ if(!group)
99+ die("%s: No such group", arg);
100+ if(setegid(group->gr_gid) < 0)
101+ die("setegid %u: %s", group->gr_gid, strerror(errno));
102+ break;
103+ }
104+#ifdef WITH_CHROOT
105+ case 'r': {
106+ arg = EARGF(usage());
107+ if(chroot(arg) < 0)
108+ die("chroot %s: %s", arg, strerror(errno));
109+ if(chdir("/") < 0)
110+ die("chdir /: %s", strerror(errno));
111+ break;
112+ }
113+#endif
114+ case 'U': {
115+ char *end;
116+ uid_t uid;
117+
118+ arg = EARGF(usage());
119+ uid = strtol(arg, &end, 10);
120+ if(arg[0] == '\0' || end[0] != '\0')
121+ usage();
122+ if(seteuid(uid) < 0)
123+ die("seteuid %u: %s", uid, strerror(errno));
124+ break;
125+ }
126+ case 'u': {
127+ struct passwd *passwd;
128+
129+ arg = EARGF(usage());
130+ passwd = getpwnam(arg);
131+ if(!passwd)
132+ die("%s: No such user", arg);
133+ if(seteuid(passwd->pw_uid) < 0)
134+ die("seteuid %u: %s", passwd->pw_uid, strerror(errno));
135+ break;
136+ }
137+ default:
138+ usage();
139+ }ARGEND
140+
141+ if(argc == 0){
142+ shcmd[0] = getenv("SHELL");
143+ if(!shcmd[0])
144+ die("$SHELL not set");
145+ cmd = shcmd;
146+ }else
147+ cmd = argv;
148+
149+ (void)execvp(cmd[0], cmd);
150+ die("execvp %s: %s", cmd[0], strerror(errno));
151+}
152+
153+__attribute((format(printf, 1, 2)))
154+static noreturn void
155+die(char *fmt, ...)
156+{
157+ va_list ap;
158+
159+ fprintf(stderr, "%s: ", argv0);
160+
161+ va_start(ap, fmt);
162+ vfprintf(stderr, fmt, ap);
163+ va_end(ap);
164+
165+ fputc('\n', stderr);
166+
167+ exit(1);
168+}
169+
170+static noreturn void
171+usage(void)
172+{
173+ fprintf(stderr, "usage: %s "
174+#if defined(HAVE_SETCLASSCONTEXT)
175+ "[-c class] "
176+#endif
177+ "[-G gid] [-g group] "
178+#if defined(WITH_CHROOT)
179+ "[-r dir] "
180+#endif
181+ "[-U uid] [-u user]\n", argv0);
182+ exit(1);
183+}