1 /*
2 * as user cmd [arg...] - run cmd with args as user on this cpu server.
3 * must be hostowner for this to work.
4 * needs #¤/caphash and #¤/capuse.
5 */
6 #include <u.h>
7 #include <libc.h>
8 #include <bio.h>
9 #include <libsec.h>
10 #include <auth.h>
11 #include "authcmdlib.h"
12
13 int debug;
14
15 int becomeuser(char*);
16 void createuser(void);
17 void *emalloc(ulong);
18 void *erealloc(void*, ulong);
19 void initcap(void);
20 int mkcmd(char*, char*, int);
21 int myauth(int, char*);
22 int qidcmp(Qid, Qid);
23 void runas(char *, char *);
24 void usage(void);
25
26 #pragma varargck argpos clog 1
27 #pragma varargck argpos fatal 1
28
29 static void
fatal(char * fmt,...)30 fatal(char *fmt, ...)
31 {
32 char msg[256];
33 va_list arg;
34
35 va_start(arg, fmt);
36 vseprint(msg, msg + sizeof msg, fmt, arg);
37 va_end(arg);
38 error("%s", msg);
39 }
40
41 void
main(int argc,char * argv[])42 main(int argc, char *argv[])
43 {
44 debug = 0;
45 ARGBEGIN{
46 case 'd':
47 debug = 1;
48 break;
49 default:
50 usage();
51 }ARGEND
52
53 initcap();
54 srand(getpid()*time(0));
55 runas(argv[0], argv[1]);
56 }
57
58 void
runas(char * user,char * cmd)59 runas(char *user, char *cmd)
60 {
61 if(becomeuser(user) < 0)
62 sysfatal("can't change uid for %s: %r", user);
63 putenv("service", "rx");
64 execl("/bin/rc", "rc", "-lc", cmd, nil);
65 sysfatal("exec /bin/rc: %r");
66 }
67
68 void *
emalloc(ulong n)69 emalloc(ulong n)
70 {
71 void *p;
72
73 if(p = mallocz(n, 1))
74 return p;
75 fatal("out of memory");
76 return 0;
77 }
78
79 void *
erealloc(void * p,ulong n)80 erealloc(void *p, ulong n)
81 {
82 if(p = realloc(p, n))
83 return p;
84 fatal("out of memory");
85 return 0;
86 }
87
88 void
usage(void)89 usage(void)
90 {
91 fprint(2, "usage: %s [-c]\n", argv0);
92 exits("usage");
93 }
94
95 void
memrandom(void * p,int n)96 memrandom(void *p, int n)
97 {
98 uchar *cp;
99
100 for(cp = (uchar*)p; n > 0; n--)
101 *cp++ = fastrand();
102 }
103
104 /*
105 * keep caphash fd open since opens of it could be disabled
106 */
107 static int caphashfd;
108
109 void
initcap(void)110 initcap(void)
111 {
112 caphashfd = open("#¤/caphash", OCEXEC|OWRITE);
113 if(caphashfd < 0)
114 fprint(2, "%s: opening #¤/caphash: %r\n", argv0);
115 }
116
117 /*
118 * create a change uid capability
119 */
120 char*
mkcap(char * from,char * to)121 mkcap(char *from, char *to)
122 {
123 uchar rand[20];
124 char *cap;
125 char *key;
126 int nfrom, nto, ncap;
127 uchar hash[SHA1dlen];
128
129 if(caphashfd < 0)
130 return nil;
131
132 /* create the capability */
133 nto = strlen(to);
134 nfrom = strlen(from);
135 ncap = nfrom + 1 + nto + 1 + sizeof(rand)*3 + 1;
136 cap = emalloc(ncap);
137 snprint(cap, ncap, "%s@%s", from, to);
138 memrandom(rand, sizeof(rand));
139 key = cap+nfrom+1+nto+1;
140 enc64(key, sizeof(rand)*3, rand, sizeof(rand));
141
142 /* hash the capability */
143 hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
144
145 /* give the kernel the hash */
146 key[-1] = '@';
147 if(write(caphashfd, hash, SHA1dlen) < 0){
148 free(cap);
149 return nil;
150 }
151
152 return cap;
153 }
154
155 int
usecap(char * cap)156 usecap(char *cap)
157 {
158 int fd, rv;
159
160 fd = open("#¤/capuse", OWRITE);
161 if(fd < 0)
162 return -1;
163 rv = write(fd, cap, strlen(cap));
164 close(fd);
165 return rv;
166 }
167
168 int
becomeuser(char * new)169 becomeuser(char *new)
170 {
171 char *cap;
172 int rv;
173
174 cap = mkcap(getuser(), new);
175 if(cap == nil)
176 return -1;
177 rv = usecap(cap);
178 free(cap);
179
180 newns(new, nil);
181 return rv;
182 }
183