1 module goinsu; 2 3 import core.sys.posix.sys.types : uid_t, gid_t; 4 5 extern(C) @nogc nothrow: 6 7 import core.stdc.errno : errno; 8 9 int setgroups(size_t size, const(gid_t)* list); 10 int getgrouplist(const(char)* user, gid_t group, gid_t* groups, int* ngroups); 11 12 auto getByNameOrId(alias byid, T, alias byname)(char* v) { 13 import core.stdc.stdlib : strtol; 14 char* end; 15 auto i = v.strtol(&end, 10); 16 return *end == '\0' ? byid(cast(T) i) : byname(v); 17 } 18 19 void fail(alias err = -1, A...)(in string fmt, A args) { 20 import core.stdc.stdio : stderr, fprintf; 21 import core.stdc.stdlib : exit; 22 stderr.fprintf(fmt.ptr, args); 23 stderr.fprintf("\n"); 24 exit(err); 25 } 26 27 static if(__traits(compiles, {import version_; string v = VERSION;})) { 28 import version_; 29 } else { 30 enum VERSION="(unknown version)"; 31 } 32 33 int main(int argc, char** argv) { 34 import core.sys.posix.unistd : getuid, getgid, execvp, setgid, setuid; 35 import core.stdc.string : strerror, strchr; 36 37 if(argc < 3) 38 ("Usage: %s user-spec command [args]\ngoinsu " ~ VERSION).fail!(0)(argv[0]); 39 40 auto user = argv[1]; 41 auto group = user.strchr(':'); 42 if(group) 43 *group++ = '\0'; // "user:group\0" ====> "user\0group\0" 44 45 import core.sys.posix.pwd : getpwuid, getpwnam; 46 auto pw = user.getByNameOrId!(getpwuid, uid_t, getpwnam); 47 48 if(pw is null && errno) 49 "Error while getting user '%s': %s" 50 .fail!errno(user, errno.strerror); 51 52 if(pw is null) 53 "User '%s' doesn't exist".fail(user); 54 55 immutable uid = pw.pw_uid; 56 auto gid = pw.pw_gid; 57 58 if(group && group[0] != '\0') { 59 import core.sys.posix.grp : getgrgid, getgrnam; 60 61 auto gr = group.getByNameOrId!(getgrgid, gid_t, getgrnam); 62 if(gr is null && errno) 63 "Error while getting group '%s': %s" 64 .fail!errno(group, user, errno.strerror); 65 66 if(gr is null) 67 "Group '%s' doesn't exist".fail(group); 68 69 gid = gr.gr_gid; 70 } 71 72 import core.sys.posix.stdlib : setenv; 73 "HOME".setenv(pw.pw_dir is null || pw.pw_dir[0] == '\0' 74 ? "/" 75 : pw.pw_dir, 1); 76 77 if(uid == getuid && gid == getgid) { 78 argv[2].execvp(&argv[2]); 79 80 return 1; 81 } 82 83 if(group && group[0] != '\0') { 84 if(1.setgroups(&gid) < 0) 85 "Error while setting group '%s': %s" 86 .fail!errno(group, errno.strerror); 87 } else { 88 import core.stdc.stdlib : realloc; 89 int ngroups; 90 gid_t* gl; 91 92 while(true) { 93 if(pw.pw_name.getgrouplist(gid, gl, &ngroups) >= 0) { 94 if(ngroups.setgroups(gl) < 0) 95 "Error while setting groups: %s" 96 .fail!errno(errno.strerror); 97 break; 98 } 99 100 gl = cast(typeof(gl)) gl.realloc(ngroups * gid_t.sizeof); 101 if(gl is null) "Out of memory".fail!(-1); 102 } 103 104 if(gl !is null) 105 gl.realloc(0); 106 } 107 108 if(gid.setgid < 0) 109 "Error while changing group: %s".fail!errno(errno.strerror); 110 111 if(uid.setuid < 0) 112 "Error while changing user: %s".fail!errno(errno.strerror); 113 114 argv[2].execvp(&argv[2]); 115 116 return 1; 117 }