aboutsummaryrefslogtreecommitdiff
path: root/src/runas.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/runas.c')
-rw-r--r--src/runas.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/runas.c b/src/runas.c
new file mode 100644
index 0000000..8d86468
--- /dev/null
+++ b/src/runas.c
@@ -0,0 +1,179 @@
+/* This file is part of NSsync
+ Copyright (C) 2017 Sergey Poznyakoff
+
+ NSsync is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ NSsync is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NSsync. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "nssync.h"
+#include <pwd.h>
+#include <grp.h>
+
+char *runas_user;
+char *runas_group;
+
+#ifndef SIZE_T_MAX
+# define SIZE_T_MAX ((size_t)-1)
+#endif
+
+static void
+addgid(gid_t **pgv, size_t *pgc, size_t *pgi, gid_t gid)
+{
+ gid_t *gv = *pgv;
+ size_t gc = *pgc;
+ size_t gi = *pgi;
+
+ if (gi == gc) {
+ if (gc == 0) {
+ gc = 16;
+ gv = grecs_calloc(gc, sizeof(*gv));
+ } else if (gc <= SIZE_T_MAX / 2 / sizeof(*gv)) {
+ gc *= 2;
+ gv = grecs_realloc(gv, gc * sizeof(*gv));
+ }
+ }
+ gv[gi++] = gid;
+ *pgv = gv;
+ *pgc = gc;
+ *pgi = gi;
+}
+
+static int
+member(gid_t *gv, size_t gc, gid_t gid)
+{
+ size_t i;
+
+ for (i = 0; i < gc; i++)
+ if (gv[i] == gid)
+ return 1;
+ return 0;
+}
+
+static size_t
+get_user_groups(const char *user, gid_t **pgv, size_t *pgc)
+{
+ struct group *gr;
+ size_t gi = 0;
+
+ setgrent();
+ while ((gr = getgrent())) {
+ char **p;
+ for (p = gr->gr_mem; *p; p++)
+ if (strcmp(*p, user) == 0)
+ addgid(pgv, pgc, &gi, gr->gr_gid);
+ }
+ endgrent();
+ return gi;
+}
+
+void
+runas(void)
+{
+ struct passwd *pw;
+ struct group *gr;
+ uid_t uid;
+ gid_t gid;
+ char const *user_name;
+ gid_t *gv;
+ size_t gc, gn;
+
+ if (!(runas_user || runas_group))
+ return;
+ if (getuid() != 0) {
+ error("not root: can't switch to user privileges");
+ exit(EX_USAGE);
+ }
+
+ user_name = runas_user;
+ if (!user_name) {
+ pw = getpwuid(0);
+ user_name = "root";
+ } else if (user_name[0] == '+') {
+ char *end;
+ unsigned long n;
+
+ errno = 0;
+ n = strtoul(user_name + 1, &end, 10);
+ if (errno || *end) {
+ error("invalid user name %s", user_name);
+ exit(EX_USAGE);
+ }
+
+ pw = getpwuid(n);
+ } else
+ pw = getpwnam(user_name);
+
+ if (!pw) {
+ error("%s: no such user", runas_user);
+ exit(EX_USAGE);
+ }
+ user_name = pw->pw_name;
+
+ uid = pw->pw_uid;
+ gid = pw->pw_gid;
+
+ if (runas_group) {
+ if (runas_group[0] == '+') {
+ char *end;
+ unsigned long n;
+
+ errno = 0;
+ n = strtoul(runas_group + 1, &end, 10);
+ if (errno || *end) {
+ error("invalid group name %s", runas_group);
+ exit(EX_USAGE);
+ }
+
+ gr = getgrgid(n);
+ } else
+ gr = getgrnam(runas_group);
+
+ if (!gr) {
+ error("%s: no such group", runas_user);
+ exit(EX_USAGE);
+ }
+
+ gid = gr->gr_gid;
+ }
+
+ gv = NULL;
+ gc = 0;
+ gn = get_user_groups(user_name, &gv, &gc);
+ if (!member(gv, gn, gid))
+ addgid(&gv, &gc, &gn, gid);
+
+ /* Reset group permissions */
+ if (setgroups(gc, gv)) {
+ error("setgroups failed: %s", strerror(errno));
+ exit(EX_UNAVAILABLE);
+ }
+ free(gv);
+
+
+ if (gid) {
+ /* Switch to the user's gid. */
+ if (setgid(gid)) {
+ error("setgid(%lu) failed: %s",
+ (unsigned long) gid, strerror(errno));
+ exit(EX_UNAVAILABLE);
+ }
+ }
+
+ /* Now reset uid */
+ if (uid) {
+ if (setuid(uid)) {
+ error("setuid(%lu) failed: %s",
+ (unsigned long) uid, strerror(errno));
+ exit(EX_UNAVAILABLE);
+ }
+ }
+}

Return to:

Send suggestions and report system problems to the System administrator.