Hello tech@,
I really like the new doas(1) utility, but after a while I started
missing the sudoedit(8) command and it didn't felt right to execute my
editor as <other user>.
I know I could just install sudo(8) from ports, but I felt valiant and
created vias(1) as a sibling to doas(1).
- It walks through the rules and tries to open the file directly after a
sete[gu]id(2). Last matching rule still wins.
- If there are files specified in the config file: No symlinks in the
path are allowed.
- The editor is opened as the user, without lingering file descriptors
- New files will be created with ownership specified by owner. If owner
is a group, both group are set and group rights will be set to rw.
- The default editor is vi, but can be overwritten via the EDITOR env.
- flags will be passed to the editor without further parsing
- The file editing will be done on a copy under /tmp
Shortcomings are:
- Only one file at a time can be edited, even if the editor supports
multiple files. If multiple files are specified only the last file will
be done on the copy.
- The copy actions are non-atomic. This is done on purpose. rename(2)
only works if the files are on the same filesystem and I don't want to
leave mkstemp(3) clutter on unexpected locations on crash. If the
application would crash during copy it leaves the original file under
/tmp. So the only risk should be when the system goes into a full hang
during the final copy. You do have backups right?
I already showed a very early concept to tedu@, who reckons it might be
a bit overkill to include in tree, but I personally reckon that only
doas(1) motivates people to open their editor as root.
Feedback is welcome.
Sincerely,
Martijn van Duren
Index: Makefile
===================================================================
RCS file: /cvs/src/usr.bin/doas/Makefile,v
retrieving revision 1.1
diff -u -p -r1.1 Makefile
--- Makefile 16 Jul 2015 20:44:21 -0000 1.1
+++ Makefile 12 Aug 2015 11:44:53 -0000
@@ -1,14 +1,8 @@
-# $OpenBSD: Makefile,v 1.1 2015/07/16 20:44:21 tedu Exp $
+# $OpenBSD: Makefile,v 1.12 2013/07/21 09:38:51 eric Exp $
-SRCS= parse.y doas.c
+.include <bsd.own.mk>
-PROG= doas
-MAN= doas.1 doas.conf.5
+SUBDIR = doas
+SUBDIR+= vias
-BINOWN= root
-BINMODE=4555
-
-CFLAGS+= -I${.CURDIR}
-COPTS+= -Wall
-
-.include <bsd.prog.mk>
+.include <bsd.subdir.mk>
Index: doas.c
===================================================================
RCS file: /cvs/src/usr.bin/doas/doas.c,v
retrieving revision 1.34
diff -u -p -r1.34 doas.c
--- doas.c 3 Aug 2015 15:31:05 -0000 1.34
+++ doas.c 12 Aug 2015 11:44:53 -0000
@@ -102,6 +102,9 @@ match(uid_t uid, gid_t *groups, int ngro
{
int i;
+ if (!(r->mode & CMD))
+ return 0;
+
if (r->ident[0] == ':') {
gid_t rgid;
if (parsegid(r->ident + 1, &rgid) == -1)
Index: doas.conf.5
===================================================================
RCS file: /cvs/src/usr.bin/doas/doas.conf.5,v
retrieving revision 1.14
diff -u -p -r1.14 doas.conf.5
--- doas.conf.5 30 Jul 2015 14:02:04 -0000 1.14
+++ doas.conf.5 12 Aug 2015 11:44:53 -0000
@@ -1,6 +1,7 @@
.\" $OpenBSD: doas.conf.5,v 1.14 2015/07/30 14:02:04 zhuk Exp $
.\"
.\"Copyright (c) 2015 Ted Unangst <t...@openbsd.org>
+.\"Copyright (c) 2015 Martijn van Duren <v...@imperialat.at>
.\"
.\"Permission to use, copy, modify, and distribute this software for any
.\"purpose with or without fee is hereby granted, provided that the above
@@ -18,11 +19,13 @@
.Os
.Sh NAME
.Nm doas.conf
-.Nd doas configuration file
+.Nd doas and vias configuration file
.Sh DESCRIPTION
The
.Xr doas 1
-utility executes commands as other users according to the rules
+and
+.Xr vias 1
+utilities execute according to the rules
in the
.Nm
configuration file.
@@ -32,8 +35,22 @@ The rules have the following format:
.Ic permit Ns | Ns Ic deny
.Op Ar options
.Ar identity
+.Ed
+.Bd -ragged -offset indent
+.Ic permit Ns | Ns Ic deny
+.Op Ar options
+.Ar identity
.Op Ic as Ar target
-.Op Ic cmd Ar command Op Ic args ...
+.Ic cmd
+.Op Ar command Op Ic args ...
+.Ed
+.Bd -ragged -offset indent
+.Ic permit Ns | Ns Ic deny
+.Op Ar options
+.Ar identity
+.Op Ic owner Ar target
+.Ic edit
+.Op Ar file ...
.Ed
.Pp
Rules consist of the following parts:
@@ -59,7 +76,9 @@ and
.Ev USERNAME .
.It Ic keepenv { Oo Ar variable ... Oc Ic }
In addition to the variables mentioned above, keep the space-separated
-specified variables.
+specified variables. The
+.Ic keepenv
+keyword is ignored with the edit keyword.
.El
.It Ar identity
The username to match.
@@ -69,6 +88,15 @@ Numeric IDs are also accepted.
.It Ic as Ar target
The target user the running user is allowed to run the command as.
The default is all users.
+.It Ic owner Ar target
+The
+.Ar target
+as whom the file is opened. If
+.Ar target
+is prepended with a colon
+.Pq Sq \&:
+then the file is opened with the uid of the user and the gid of
+.Ar target .
.It Ic cmd Ar command
The command the user is allowed or denied to run.
The default is all commands.
@@ -80,8 +108,23 @@ need to match for the command to be succ
Specifying
.Ic args
alone means that command should be run without any arguments.
+.It Ic file
+One or more filenames that may be opened by this statement. Only one
+.Ic file
+at a time may be opened by
+.Xr vias 1 .
.El
.Pp
+When no
+.Ic cmd
+or
+.Ic edit
+keywords are present, the rule permits unlimited
+.Xr doas 1
+and
+.Xr vias 1
+usage.
+.Pp
The last matching rule determines the action taken.
.Pp
Comments can be put anywhere in the file using a hash mark
@@ -123,6 +166,7 @@ permit nopass keepenv { \e
SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc
permit nopass keepenv { ENV PS1 SSH_AUTH_SOCK } :wheel
permit nopass tedu as root cmd /usr/sbin/procmap
+permit nopass tedu owner root edit /etc/hosts
.Ed
.Sh SEE ALSO
.Xr doas 1
@@ -133,3 +177,4 @@ configuration file first appeared in
.Ox 5.8 .
.Sh AUTHORS
.An Ted Unangst Aq Mt t...@openbsd.org
+.An Martijn van Duren Aq Mt v...@imperialat.at
Index: doas.h
===================================================================
RCS file: /cvs/src/usr.bin/doas/doas.h,v
retrieving revision 1.4
diff -u -p -r1.4 doas.h
--- doas.h 24 Jul 2015 06:36:42 -0000 1.4
+++ doas.h 12 Aug 2015 11:44:53 -0000
@@ -4,10 +4,19 @@ struct rule {
int action;
int options;
const char *ident;
- const char *target;
- const char *cmd;
- const char **cmdargs;
- const char **envlist;
+ int mode;
+ union {
+ const char *target;
+ const char *owner;
+ };
+ union {
+ struct {
+ const char *cmd;
+ const char **cmdargs;
+ const char **envlist;
+ };
+ const char **files;
+ };
};
extern struct rule **rules;
@@ -21,3 +30,6 @@ size_t arraylen(const char **);
#define NOPASS 0x1
#define KEEPENV 0x2
+
+#define CMD 0x1
+#define EDIT 0x2
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.bin/doas/parse.y,v
retrieving revision 1.11
diff -u -p -r1.11 parse.y
--- parse.y 28 Jul 2015 21:36:03 -0000 1.11
+++ parse.y 12 Aug 2015 11:44:53 -0000
@@ -1,6 +1,7 @@
/* $OpenBSD: parse.y,v 1.11 2015/07/28 21:36:03 deraadt Exp $ */
/*
* Copyright (c) 2015 Ted Unangst <t...@openbsd.org>
+ * Copyright (c) 2015 Martijn van Duren <v...@imperialat.at>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -16,8 +17,10 @@
*/
%{
+#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
+#include <libgen.h>
#include <unistd.h>
#include <stdint.h>
#include <stdarg.h>
@@ -32,8 +35,14 @@ typedef struct {
struct {
int action;
int options;
- const char *cmd;
- const char **cmdargs;
+ int mode;
+ union {
+ struct {
+ const char *cmd;
+ const char **cmdargs;
+ };
+ const char **files;
+ };
const char **envlist;
};
const char *str;
@@ -55,7 +64,7 @@ int yyparse(void);
%}
-%token TPERMIT TDENY TAS TCMD TARGS
+%token TPERMIT TDENY TAS TOWNER TCMD TEDIT TARGS
%token TNOPASS TKEEPENV
%token TSTRING
@@ -79,6 +88,29 @@ rule: action ident target cmd {
r->target = $3.str;
r->cmd = $4.cmd;
r->cmdargs = $4.cmdargs;
+ r->mode = $4.mode;
+ if (nrules == maxrules) {
+ if (maxrules == 0)
+ maxrules = 63;
+ else
+ maxrules *= 2;
+ if (!(rules = reallocarray(rules, maxrules,
+ sizeof(*rules))))
+ errx(1, "can't allocate rules");
+ }
+ rules[nrules++] = r;
+ } | action ident owner edit {
+ struct rule *r;
+ r = calloc(1, sizeof(*r));
+ if (!r)
+ errx(1, "can't allocate rule");
+ r->action = $1.action;
+ r->options = $1.options;
+/* envlist is ignore */
+ r->ident = $2.str;
+ r->owner = $3.str;
+ r->files = $4.files;
+ r->mode = $4.mode;
if (nrules == maxrules) {
if (maxrules == 0)
maxrules = 63;
@@ -143,12 +175,26 @@ target: /* optional */ {
$$.str = $2.str;
} ;
-cmd: /* optional */ {
+owner: /* optional */ {
+ $$.str = NULL;
+ } | TOWNER TSTRING {
+ $$.str = $2.str;
+ } ;
+
+cmd: TCMD cmdval {
+ $$.mode = $2.mode;
+ $$.cmd = $2.str;
+ $$.cmdargs = $2.cmdargs;
+ } ;
+
+cmdval: /* optional */ {
$$.cmd = NULL;
$$.cmdargs = NULL;
- } | TCMD TSTRING args {
+ $$.mode = CMD|EDIT;
+ } | TSTRING args {
$$.cmd = $2.str;
$$.cmdargs = $3.cmdargs;
+ $$.mode = CMD;
} ;
args: /* empty */ {
@@ -169,6 +215,30 @@ argslist: /* empty */ {
$$.cmdargs[nargs + 1] = NULL;
} ;
+edit: TEDIT files {
+ $$.mode = EDIT;
+ if (arraylen($2.files))
+ $$.files = $2.files;
+ else {
+ free($2.files);
+ $$.files = NULL;
+ }
+ } ;
+
+files: /* empty */ {
+ if (!($$.files = calloc(1, sizeof(char *))))
+ errx(1, "can't allocate files");
+ } | files TSTRING {
+ int nargs = arraylen($1.files);
+ if ($2.str[0] != '/')
+ yyerror("file %s can't be relative", $2.str);
+ if (!($$.files = reallocarray($1.files, nargs + 2,
+ sizeof(char *))))
+ errx(1, "can't allocate args");
+ $$.files[nargs] = $2.str;
+ $$.files[nargs + 1] = NULL;
+ } ;
+
%%
void
@@ -190,9 +260,11 @@ struct keyword {
{ "deny", TDENY },
{ "permit", TPERMIT },
{ "as", TAS },
+ { "owner", TOWNER },
{ "cmd", TCMD },
{ "args", TARGS },
{ "nopass", TNOPASS },
+ { "edit", TEDIT },
{ "keepenv", TKEEPENV },
};
Index: vias.1
===================================================================
RCS file: vias.1
diff -N vias.1
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ vias.1 12 Aug 2015 11:44:53 -0000
@@ -0,0 +1,86 @@
+.\" $OpenBSD: doas.1,v 1.14 2015/07/27 17:57:06 jmc Exp $
+.\"
+.\"Copyright (c) 2015 Ted Unangst <t...@openbsd.org>
+.\"Copyright (c) 2015 Martijn van Duren <v...@imperialat.at>
+.\"
+.\"Permission to use, copy, modify, and distribute this software for any
+.\"purpose with or without fee is hereby granted, provided that the above
+.\"copyright notice and this permission notice appear in all copies.
+.\"
+.\"THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.Dd $Mdocdate: July 27 2015 $
+.Dt VIAS 1
+.Os
+.Sh NAME
+.Nm vias
+.Nd write a file owner by another user
+.Sh SYNOPSIS
+.Nm vias
+.Op "editor options"
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a copy of the specified file as another user and opens
it in the
+favorite editor as the user executing the command.
+The
+.Ar "editor options"
+are passed to the editor without modification.
+.Pp
+If the path to
+.Ar file
+contains a symlink in one of it's components, it will result in a
permission denied.
+.Sh ENVIRONMENT
+If the following environment variable exists it will be utilized by
+.Nm :
+.Bl -tag -width EDITOR
+.It Ev EDITOR
+The editor specified by the string
+.Ev EDITOR
+will be invoked instead of the default editor
+.Xr vi 1 .
+.El
+.Sh EXIT STATUS
+.Ex -std
+It may fail for one of the following reasons:
+.Pp
+.Bl -bullet -compact
+.It
+The config file
+.Pa /etc/doas.conf
+could not be parsed.
+.It
+The user specified file could not be opened.
+.It
+The password was incorrect.
+.It
+The editor exited with a error status.
+.El
+.Sh SEE ALSO
+.Xr vi 1 ,
+.Xr doas.conf 5
+.Sh AUTHORS
+.An Ted Unangst Aq Mt t...@openbsd.org
+.An Martijn van Duren Aq Mt v...@imperialat.at
+.Sh BUGS
+.Bl -bullet -compact
+.It
+Only one
+.Ar file
+can be edited at a time, extra files will be opened with the rights of
the user.
+.It
+.Ar file
+isn't copied back atomically. This means that if the application crashes
+.Ar file
+could become corrupted. In those cases a copy of
+.Ar file
+should be available under
+.Pa /tmp ,
+as pointed out in the error message.
+.El
Index: vias.c
===================================================================
RCS file: vias.c
diff -N vias.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ vias.c 12 Aug 2015 11:44:53 -0000
@@ -0,0 +1,424 @@
+/* $OpenBSD: doas.c,v 1.34 2015/08/03 15:31:05 tedu Exp $ */
+/*
+ * Copyright (c) 2015 Ted Unangst <t...@openbsd.org>
+ * Copyright (c) 2015 Martijn van Duren <v...@imperialat.at>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <login_cap.h>
+#include <bsd_auth.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include "doas.h"
+
+static void __dead
+usage(void)
+{
+ fprintf(stderr, "usage: vias [editor options] file\n");
+ exit(1);
+}
+
+size_t
+arraylen(const char **arr)
+{
+ size_t cnt = 0;
+
+ while (*arr) {
+ cnt++;
+ arr++;
+ }
+ return cnt;
+}
+
+static int
+parseuid(const char *s, uid_t *uid)
+{
+ struct passwd *pw;
+ const char *errstr;
+
+ if ((pw = getpwnam(s)) != NULL) {
+ *uid = pw->pw_uid;
+ return 0;
+ }
+ *uid = strtonum(s, 0, UID_MAX, &errstr);
+ if (errstr)
+ return -1;
+ return 0;
+}
+
+static int
+uidcheck(const char *s, uid_t desired)
+{
+ uid_t uid;
+
+ if (parseuid(s, &uid) != 0)
+ return -1;
+ if (uid != desired)
+ return -1;
+ return 0;
+}
+
+static int
+parsegid(const char *s, gid_t *gid)
+{
+ struct group *gr;
+ const char *errstr;
+
+ if ((gr = getgrnam(s)) != NULL) {
+ *gid = gr->gr_gid;
+ return 0;
+ }
+ *gid = strtonum(s, 0, GID_MAX, &errstr);
+ if (errstr)
+ return -1;
+ return 0;
+}
+
+static inline int
+containssym(const char *path)
+{
+ struct stat sb;
+
+ if (path[1] == '\0')
+ return 0;
+
+ if (lstat(path, &sb) == -1) {
+ if (errno != ENOENT)
+ return 1;
+ else
+ return containssym(dirname(path));
+ }
+
+ return (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) ?
+ containssym(dirname(path)) : 1;
+}
+
+#define BUFSIZE 2048
+static int
+fcpy(int from, int to)
+{
+ int nread;
+ char buf[BUFSIZE];
+
+/* file descriptors should be validated before copying */
+ assert(lseek(from, 0, SEEK_SET) == 0);
+ if(ftruncate(to, 0) == -1)
+ err(1, "ftruncate");
+ assert(lseek(to, 0, SEEK_SET) == 0);
+ while((nread = read(from, buf, BUFSIZE)) > 0) {
+ if(write(to, buf, nread) != nread)
+ return 0;
+ }
+ return (nread == -1) ? 0 : 1;
+}
+
+static int
+match(uid_t uid, gid_t *groups, int ngroups, const char *file, int *fd,
+ const struct stat *sb, struct rule *r)
+{
+ struct stat rsb, tsb;
+ int i;
+ int fexists = 1;
+ char rrfile[PATH_MAX];
+ char rfile[PATH_MAX];
+ uid_t ruid = 0;
+ gid_t rgid, gid = 0;
+
+ if (!(r->mode & EDIT))
+ return 0;
+
+ if (!sb)
+ fexists = 0;
+
+ if (r->ident[0] == ':') {
+ if (parsegid(r->ident + 1, &rgid) == -1)
+ return 0;
+ for (i = 0; i < ngroups; i++) {
+ if (rgid == groups[i])
+ break;
+ }
+ if (i == ngroups)
+ return 0;
+ } else {
+ if (uidcheck(r->ident, uid) != 0)
+ return 0;
+ }
+
+ if (r->files) {
+ for (i = 0; r->files[i]; i++) {
+ if (stat(r->files[i], &rsb) == 0) {
+ if (!fexists)
+ continue;
+ if (rsb.st_ino == sb->st_ino &&
+ rsb.st_dev == sb->st_dev)
+ break;
+ } else {
+ if (errno != ENOENT || fexists)
+ continue;
+ if (realpath(file, rfile) == NULL ||
+ realpath(r->files[i], rrfile) == NULL)
+ continue;
+ if (strncmp(rfile, rrfile, PATH_MAX) == 0)
+ break;
+ }
+ }
+ if (!r->files[i])
+ return 0;
+ if (containssym(r->files[i]))
+ return 0;
+ }
+
+ if (r->owner) {
+ gid = getegid();
+ if (r->owner[0] == ':') {
+ if (parsegid(r->owner + 1, &rgid) == -1)
+ return 0;
+/* setegid and seteuid should work, since euid should be 0 */
+ assert(setegid(rgid) == 0);
+ assert(seteuid(uid) == 0);
+ } else {
+ if (parseuid(r->owner, &ruid) == -1)
+ return 0;
+ assert(seteuid(ruid) == 0);
+ }
+ }
+
+ *fd = open(file, O_RDWR | O_CREAT, 0666);
+
+ if (r->owner) {
+ assert(seteuid(0) == 0);
+ assert(setegid(gid) == 0);
+ if (!fexists && *fd != -1 && r->owner[0] == ':') {
+/* Make sure that new files remains writable for group owner, if newly
created, based on group rights */
+ if (fchown(*fd, -1, rgid) == -1 ||
+ fstat(*fd, &tsb) == -1 ||
+ fchmod(*fd, ((tsb.st_mode | 060) & 07777)) == -1) {
+ (void)unlink(file);
+ close(*fd);
+ *fd = -1;
+ }
+ }
+ }
+
+ return (*fd == -1) ? 0 : 1;
+}
+
+static int
+permit(uid_t uid, gid_t *groups, int ngroups, struct rule **lastr,
+ const char *file, int *fd)
+{
+ struct stat sb, *psb = NULL;
+ int i;
+ int tfd = -1;
+
+ if (stat(file, &sb) == 0) {
+ if(!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))
+ return 0;
+ psb = &sb;
+ } else if (errno != ENOENT)
+ return 0;
+
+ *lastr = NULL;
+ for (i = 0; i < nrules; i++) {
+ if (match(uid, groups, ngroups, file, fd, psb, rules[i])) {
+ *lastr = rules[i];
+ if (tfd != -1)
+ (void)close(tfd);
+ tfd = *fd;
+ }
+ }
+ if (!*lastr)
+ return 0;
+ return (*lastr)->action == PERMIT;
+}
+
+static void
+parseconfig(const char *filename, int checkperms)
+{
+ extern FILE *yyfp;
+ extern int yyparse(void);
+ struct stat sb;
+
+ yyfp = fopen(filename, "r");
+ if (!yyfp) {
+ if (checkperms)
+ fprintf(stderr, "doas is not enabled.\n");
+ else
+ warn("could not open config file");
+ exit(1);
+ }
+
+ if (checkperms) {
+ if (fstat(fileno(yyfp), &sb) != 0)
+ err(1, "fstat(\"%s\")", filename);
+ if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
+ errx(1, "%s is writable by group or other", filename);
+ if (sb.st_uid != 0)
+ errx(1, "%s is not owned by root", filename);
+ }
+
+ yyparse();
+ fclose(yyfp);
+ if (parse_errors)
+ exit(1);
+}
+
+static void __dead
+fail(void)
+{
+ fprintf(stderr, "Permission denied\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *editor;
+ char cmdline[LINE_MAX];
+ char tfile[PATH_MAX];
+ char *file, *bfile;
+ char myname[_PW_NAME_LEN + 1];
+ struct passwd *pw;
+ struct rule *rule;
+ struct stat tsb, tsb2;
+ uid_t uid;
+ gid_t groups[NGROUPS_MAX + 1];
+ int ngroups;
+ int i;
+ int tfd, fd = -1;
+ int status;
+
+ if (argc == 1)
+ usage();
+
+ uid = getuid();
+
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ if (ngroups == -1)
+ err(1, "can't get groups");
+ groups[ngroups++] = getgid();
+
+ if(setuid(0) == -1)
+ errx(1, "vias not executed as root");
+
+ pw = getpwuid(uid);
+ if (!pw)
+ err(1, "getpwuid failed");
+ if (strlcpy(myname, pw->pw_name, sizeof(myname)) >= sizeof(myname))
+ errx(1, "pw_name too long");
+
+ parseconfig("/etc/doas.conf", 1);
+
+ if ((editor = getenv("EDITOR")) == NULL || editor[0] == '\0')
+ editor = "vi";
+
+ /* cmdline is used only for logging, no need to abort on truncate */
+ (void) strlcpy(cmdline, editor, sizeof(cmdline));
+ for (i = 1; i < argc; i++) {
+ if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline))
+ break;
+ if (strlcat(cmdline, argv[i], sizeof(cmdline)) >=
sizeof(cmdline))
+ break;
+ }
+
+ file = argv[argc-1];
+
+ if (!permit(uid, groups, ngroups, &rule, file, &fd)) {
+ syslog(LOG_AUTHPRIV | LOG_NOTICE,
+ "failed command for %s: %s", myname, cmdline);
+ fail();
+ }
+
+ if (!(rule->options & NOPASS)) {
+ if (!auth_userokay(myname, NULL, NULL, NULL)) {
+ syslog(LOG_AUTHPRIV | LOG_NOTICE,
+ "failed password for %s", myname);
+ fail();
+ }
+ }
+
+ if ((bfile = basename(file)) == NULL)
+ err(1, "basename");
+ if (snprintf(tfile, PATH_MAX, "/tmp/%s.XXXXXX", bfile) >= PATH_MAX)
+ errx(1, "Could not create temporary name");
+
+ if ((tfd = mkstemp(tfile)) == -1)
+ err(1, "mkstemp");
+ if (fchown(tfd, uid, -1) == -1) {
+ (void)unlink(tfile);
+ err(1, "fchown");
+ }
+
+ if (!fcpy(fd, tfd))
+ err(1, "fcpy");
+
+ if (fstat(tfd, &tsb) == -1) {
+ (void)unlink(tfile);
+ err(1, "fstat");
+ }
+
+ switch (fork()) {
+ case -1:
+ (void)unlink(tfile);
+ err(1, "fork");
+ case 0:
+ (void)close(tfd);
+ (void)close(fd);
+ setuid(uid);
+ argv[0] = editor;
+ argv[argc-1] = tfile;
+ execvp(editor, argv);
+ err(1, "execvp");
+ }
+ if (wait(&status) == -1) {
+ (void)unlink(tfile);
+ err(1, "wait");
+ }
+ if (status != 0) {
+ (void)unlink(tfile);
+ errx(1, "Editor %s exited with error code %d",
+ editor, status);
+ }
+
+ if (fstat(tfd, &tsb2) == -1)
+ err(1, "stat (temp file %s preserved)", tfile);
+
+ if (tsb.st_mtime == tsb2.st_mtime && tsb.st_size == tsb2.st_size) {
+ (void)unlink(tfile);
+ warnx("temporary file not modified");
+ exit(0);
+ }
+ if (!fcpy(tfd, fd))
+ err(1, "fcpy (temp file %s preserved)", tfile);
+ (void)close(fd);
+ (void)close(tfd);
+ (void)unlink(tfile);
+ exit(0);
+}
Index: doas/Makefile
===================================================================
RCS file: doas/Makefile
diff -N doas/Makefile
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ doas/Makefile 12 Aug 2015 11:44:54 -0000
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.1 2015/07/16 20:44:21 tedu Exp $
+
+.PATH: ${.CURDIR}/..
+
+SRCS= parse.y doas.c
+
+PROG= doas
+MAN= doas.1 doas.conf.5
+
+BINDIR= /usr/bin
+BINOWN= root
+BINMODE=4555
+
+CFLAGS+= -I${.CURDIR}/..
+COPTS+= -Wall
+
+.include <bsd.prog.mk>
Index: vias/Makefile
===================================================================
RCS file: vias/Makefile
diff -N vias/Makefile
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ vias/Makefile 12 Aug 2015 11:44:54 -0000
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.1 2015/07/16 20:44:21 tedu Exp $
+
+.PATH: ${.CURDIR}/..
+
+SRCS= parse.y vias.c
+
+PROG= vias
+MAN= vias.1 doas.conf.5
+
+BINDIR= /usr/bin
+BINOWN= root
+BINMODE=4555
+
+CFLAGS+= -I${.CURDIR}/..
+COPTS+= -Wall
+
+.include <bsd.prog.mk>