Commit | Line | Data |
---|---|---|
4dc81955 ES |
1 | # include "defs.h" |
2 | /* must be setuid root */ | |
3 | /* | |
4 | net - -b -c cmd -f -i file -l name -mmach -n -o file -p passwd | |
5 | -r file -s file -u uid -w -x -y -z command | |
6 | ||
7 | - take from standard input | |
8 | -b never send anything back | |
9 | -c cmd think of this as a "cmd" * | |
10 | -f force prompting of user name and password | |
11 | -i file remote stdin * | |
12 | -l name remote login name | |
13 | -m Mach remote machine | |
14 | -n do not write back anything, always mail them back | |
15 | -o file remote stdout & stderr * | |
16 | -p pass remote password | |
17 | -q quiet option, send back only if rcode !=0 or if there is stdout | |
18 | -r file local response file | |
19 | -s file local stdin file * | |
20 | ||
21 | (super users only, always skip login/passwd check:) | |
22 | -u uid net queue files should be owned by uid (16 bits) | |
23 | -w this is a write/mail response cmd * | |
24 | -x this is being forwarded through us to another machine * | |
25 | -y skip login/password check * | |
26 | -z this is a response file being returned * | |
27 | ||
28 | * = not documented in net(NEW) | |
29 | ||
30 | */ | |
31 | /* | |
32 | code option reason | |
33 | q normal request | |
34 | w -w message to be written back | |
35 | -x being forwarded through us | |
36 | y -y simply skips login check (used by netlpr) | |
37 | s -z normal response | |
38 | */ | |
39 | static char dfname[]= DFNAME; | |
40 | ||
41 | main(argc, argv) | |
42 | char **argv; { | |
43 | register int i; | |
44 | int outerror(),uid; | |
45 | char *genparmlist(); | |
46 | char resp[FNS], infile[FNS], outfile[FNS], localin[FNS]; | |
47 | char buf[BUFSIZ], suid[10]; | |
48 | char sin =0, code, zopt = 0, wopt = 0, yopt = 0, xopt = 0; | |
49 | char *s,*sn; | |
50 | char sTtyname[20], sCmdAct[BUFSIZ], sCmdVirt[BUFSIZ]; | |
51 | long cnt = 0L, maxfile = MAXFILE, lTtytime; | |
52 | char cflag = 'a'; | |
53 | FILE *file, *temp, *rfile; | |
54 | struct utmp utmpstr; | |
55 | struct stat statbuf; | |
56 | ||
57 | debugflg = DBV; | |
58 | argv[argc] = 0; | |
59 | sCmdAct[0] = resp[0] = outfile[0] = infile[0] = 0; | |
60 | sCmdVirt[0] = localin[0] = 0; | |
61 | sTtyname[0] = 0; | |
62 | suid[0] = 0; | |
63 | ||
64 | if(isatty(0)) strcat(sTtyname,ttyname(0)); | |
65 | else if(isatty(2)) strcat(sTtyname,ttyname(2)); | |
66 | remote = 0; | |
67 | signal(SIGHUP,outerror); | |
68 | signal(SIGQUIT,outerror); | |
69 | signal(SIGINT,outerror); | |
70 | signal(SIGTRM,outerror); | |
71 | ||
72 | while(argc > 1 && argv[1][0] == '-'){ | |
73 | argc--; argv++; | |
74 | switch(argv[0][1]){ | |
75 | case 0: sin++; break; | |
76 | case 'b': status.nonotify++; break; | |
77 | case 'c': harg(sCmdVirt,&argc,&argv); break; | |
78 | case 'f': status.force++; break; | |
79 | case 'i': harg(infile,&argc,&argv); break; | |
80 | case 'l': harg(status.login,&argc,&argv); break; | |
81 | case 'm': harg(buf,&argc,&argv); remote = lookup(buf); break; | |
82 | case 'n': status.nowrite++; break; | |
83 | case 'o': harg(outfile,&argc,&argv); break; | |
84 | case 'p': | |
85 | harg(status.mpasswd,&argc,&argv); | |
86 | if(status.mpasswd[0] == 0) | |
87 | strcpy(status.mpasswd,"\n\n"); | |
88 | break; | |
89 | case 'q': status.quiet++; break; | |
90 | case 'r': harg(buf,&argc,&argv); addir(resp,buf); break; | |
91 | case 's': harg(localin,&argc,&argv); break; | |
92 | case 'u': harg(suid,&argc,&argv); break; | |
93 | case 'w': wopt++; break; | |
94 | case 'x': xopt++; break; | |
95 | case 'y': yopt++; break; | |
96 | case 'z': zopt++; break; | |
97 | default: | |
98 | fprintf(stderr,"Unknown option %s\n",argv[0]); | |
99 | break; | |
100 | } | |
101 | } | |
102 | while(argc > 1){ | |
103 | argc--; argv++; | |
104 | strcat(sCmdAct,argv[0]); | |
105 | strcat(sCmdAct," "); | |
106 | } | |
107 | uid = getuid(); | |
108 | code = 'q'; | |
109 | if(zopt || wopt || yopt || xopt || suid[0] != 0){ | |
110 | /* check z or w or y or x option permission */ | |
111 | # ifndef TESTING | |
112 | if(uid != SUPERUSER){ | |
113 | fprintf(stderr,"Error: Not super-user"); | |
114 | outerror(); | |
115 | } | |
116 | # endif | |
117 | code = zopt ? 's' : 'w'; | |
118 | code = yopt ? 'y' : code; | |
119 | if(status.mpasswd[0] == 0) /* no passwd required */ | |
120 | strcpy(status.mpasswd,"\n"); | |
121 | } | |
122 | ||
123 | status.jobno = 32767; /* default (invalid) job number */ | |
124 | if(code == 'q' && !xopt){ | |
125 | if((sn = SnCurrent()) == NULL | |
126 | /* || machtype[local-'a'] == M_CC */) | |
127 | /* turns out we never use jobno, except in netlpr */ | |
128 | /* read passwd file, get status.localname & jobno */ | |
129 | passwdent(); | |
130 | else | |
131 | /* don't bother reading passwd file, don't need jobno */ | |
132 | strcpy(status.localname,sn); | |
133 | } | |
134 | ||
135 | /* sets remote,status.login,status.force,status.mpasswd, | |
136 | status.nonotify, status.nowrite */ | |
137 | /* may read passwd file if getenv(HOME) reads it */ | |
138 | commandfile(); | |
139 | ||
140 | if(remote == 0)remote = getremote(local); | |
141 | # ifndef TESTING | |
142 | if(remote == local){ | |
143 | fprintf(stderr,"Request sent to local machine - doesn't make sense\n"); | |
144 | /* outerror(); */ | |
145 | } | |
146 | # endif | |
147 | strcat(status.defcmd," "); | |
148 | if(strlen(sCmdAct) == 0)strcpy(sCmdAct,status.defcmd); | |
149 | sCmdAct[strlen(sCmdAct)-1] = 0; | |
150 | mktemp(dfname); | |
151 | /* determine through machine */ | |
152 | i = gothru(local,remote); | |
153 | if(i == 0){ | |
154 | s = longname(remote); | |
155 | if(s != 0)fprintf(stderr,"No path to %s machine.\n",s); | |
156 | else fprintf(stderr,"Unknown machine\n"); | |
157 | outerror(); | |
158 | } | |
159 | dfname[strlen(dfname)-11] = i; /* set directory */ | |
160 | dfname[strlen(dfname)-7] = i; /* set file (unused) */ | |
161 | /* check to see if data files are directories */ | |
162 | if(isdirectory(resp) || isdirectory(infile) || isdirectory(outfile)){ | |
163 | fprintf(stderr,"%s is a directory, must be a file\n", | |
164 | isdirectory(resp) ? resp : | |
165 | isdirectory(infile) ? infile : | |
166 | outfile); | |
167 | outerror(); | |
168 | } | |
169 | if(suid[0] != 0)uid = atoi(suid); | |
170 | if(resp[0]){ | |
171 | if(strcmp(resp,"/dev/tty") == 0){ | |
172 | fprintf(stderr,"Can't have /dev/tty as response file.\n"); | |
173 | outerror(); | |
174 | } | |
175 | if(stat(resp,&statbuf) == -1){ | |
176 | strcpy(buf,resp); | |
177 | s = &buf[0]; | |
178 | s = s + strlen(buf) - 1; | |
179 | while(*s != '/' && s > &(buf[0]))s--; | |
180 | *s = 0; | |
181 | debug("chkdir %s",buf); | |
182 | if(strlen(buf) == 0)strcpy(buf,"."); | |
183 | if(access(buf,2) == -1){ | |
184 | perror(buf); | |
185 | outerror(); | |
186 | } | |
187 | if((rfile=fopen(resp,"w")) == NULL){ | |
188 | perror(resp); | |
189 | outerror(); | |
190 | } | |
191 | chmod(resp,0600); | |
192 | fclose(rfile); | |
193 | mchown(resp,uid,getgid()); | |
194 | } | |
195 | else if(access(resp,2) == -1){ | |
196 | perror(resp); | |
197 | outerror(); | |
198 | } | |
199 | } | |
200 | /* go ahead and prompt for login name and passwd, if neccessary, | |
201 | as long as the X option has not been specified */ | |
202 | if(code == 'q' && !xopt)promptlogin(remote); | |
203 | ||
204 | /* at this point, we create the dfa... file */ | |
205 | file = fopen(dfname,"w"); | |
206 | if(file == NULL){ | |
207 | perror(dfname); | |
208 | outerror(); | |
209 | } | |
210 | chmod(dfname,0600); | |
211 | mchown(dfname,uid,getgid()); | |
212 | if(xopt)goto stickit; | |
213 | if(status.mpasswd[0] == '\n') | |
214 | status.mpasswd[0] = 0; | |
215 | # ifndef NEWPROT | |
216 | if(machtype[local-'a'] == M_CC && machtype[remote-'a'] == M_CC | |
217 | && status.mpasswd[0] != 0){ | |
218 | s = crypt(status.mpasswd); | |
219 | strcpy(status.mpasswd,s); | |
220 | } | |
221 | # endif | |
222 | if(status.mpasswd[0] == 0 && code == 'q' && | |
223 | strcmp(status.login,"network") != 0){ | |
224 | fprintf(stderr,"Zero-length password not allowed\n"); | |
225 | outerror(); | |
226 | } | |
227 | if(code == 'q' && (streql(status.login,"root") == 0 || | |
228 | streql(status.login,"ruut") == 0)){ | |
229 | fprintf(stderr,"Can't login as root through the network\n"); | |
230 | outerror(); | |
231 | } | |
232 | # ifdef SPACCT | |
233 | /* handle special accounts */ | |
234 | /* give a value for mgid and muid */ | |
235 | strcpy(status.mpasswd,handlesp(status.login,status.mpasswd, | |
236 | status.localname,status.muid,status.mgid)); | |
237 | # endif | |
238 | enmask(status.mpasswd); | |
239 | lTtytime = 0; | |
240 | if(sTtyname[0] && status.nowrite == 0){ | |
241 | temp = fopen("/etc/utmp","r"); | |
242 | if(temp == NULL){ | |
243 | perror("/etc/utmp"); | |
244 | outerror(); | |
245 | } | |
246 | while(fread(&utmpstr,1,sizeof utmpstr,temp) == sizeof utmpstr) | |
247 | # ifdef OLDTTY | |
248 | if(utmpstr.ut_tty == sTtyname[8]){ | |
249 | # else | |
250 | if(strcmp(utmpstr.ut_line,sTtyname+5) == 0){ | |
251 | # endif | |
252 | lTtytime = utmpstr.ut_time; | |
253 | break; | |
254 | } | |
255 | } | |
256 | /* | |
257 | debug("p:%s:\n",status.mpasswd); | |
258 | */ | |
259 | /* cflag is initially 'a'. Add the flags as needed. */ | |
260 | if(status.nonotify)cflag += F_NONOTIFY; | |
261 | if(status.quiet)cflag += F_QUIET; | |
262 | /* | |
263 | protocol: | |
264 | code, remote mach, local mach, version stamp (2), remote login name, | |
265 | password, -i, -o, -r files, | |
266 | local login name, terminal, flag, utmp tty login time, | |
267 | cc jobno(variable parameter list), current time, | |
268 | command '\n' real command '\n' | |
269 | any data | |
270 | ||
271 | changes: | |
272 | 1) remove header | |
273 | 3) use ascii length instead of 4 bytes | |
274 | 4) encrypt the login name, command, and part of data as well | |
275 | */ | |
276 | ||
277 | fprintf(file, | |
278 | "%c :%c :%c :%c :%c :%s :%s :%s :%s :%s :%s :%s :%c :%lo :%d%s :%ld :", | |
279 | code,remote,local,VMAJOR+'a',VMINOR+'a',status.login, | |
280 | status.mpasswd,infile,outfile,resp, | |
281 | status.localname,sTtyname,cflag,lTtytime, | |
282 | status.jobno,genparmlist(),gettime()-TIMEBASE); | |
283 | fputs(sCmdAct,file); | |
284 | putc('\n',file); | |
285 | fputs(sCmdVirt,file); | |
286 | putc('\n',file); | |
287 | stickit: | |
288 | /* between ingres machines, allow long files */ | |
289 | /* this should be parametrized on a per machine pair basis */ | |
290 | if(machtype[local - 'a'] == M_INGRES && | |
291 | machtype[remote - 'a'] == M_INGRES) | |
292 | maxfile = MAXFILELARGE; | |
293 | if(sin) | |
294 | while((i = fread(buf,1,BUFSIZ,stdin)) > 0){ | |
295 | if(fwrite(buf,1,i,file) != i){ | |
296 | perror("net queue file"); | |
297 | outerror(); | |
298 | } | |
299 | if((cnt += i) > maxfile)goto toobig; | |
300 | if(feof(stdin))break; | |
301 | } | |
302 | else if(localin[0]){ | |
303 | if(access(localin,4) == -1){ | |
304 | perror(localin); | |
305 | outerror(); | |
306 | } | |
307 | temp = fopen(localin,"r"); | |
308 | if(temp == NULL){ | |
309 | perror(localin); | |
310 | outerror(); | |
311 | } | |
312 | while((i = fread(buf,1,BUFSIZ,temp)) > 0){ | |
313 | if((cnt += i) > maxfile)goto toobig; | |
314 | if(fwrite(buf,1,i,file) != i){ | |
315 | perror("net queue file"); | |
316 | outerror(); | |
317 | } | |
318 | } | |
319 | fclose(temp); | |
320 | } | |
321 | fclose(file); | |
322 | chmod(dfname,0400); | |
323 | dfname[strlen(dfname)-9] = 'c'; | |
324 | file = fopen(dfname,"w"); | |
325 | chmod(dfname,0400); | |
326 | fclose(file); | |
327 | mchown(dfname,uid,getgid()); | |
328 | exit(0); | |
329 | toobig: | |
330 | fprintf(stderr,"No more than %ld bytes can be sent\n",maxfile); | |
331 | outerror(); /* no return */ | |
332 | } | |
333 | /* | |
334 | called if there is an error, makes sure that the files created | |
335 | are deleted and the terminal is reset to echo | |
336 | */ | |
337 | outerror(){ | |
338 | register int i; | |
339 | struct sgttyb stt; | |
340 | signal(SIGHUP,SIG_IGN); signal(SIGINT,SIG_IGN); | |
341 | signal(SIGQUIT,SIG_IGN); signal(SIGTRM,SIG_IGN); | |
342 | unlink(dfname); | |
343 | i = strlen(dfname) - 9; | |
344 | dfname[i] = (dfname[i] == 'c' ? 'd' : 'c'); | |
345 | unlink(dfname); | |
346 | if(gtty(0,&stt) >= 0){ | |
347 | stt.sg_flags |= ECHO; | |
348 | stty(0,&stt); | |
349 | } | |
350 | exit(1); | |
351 | } | |
352 | enmask(s) | |
353 | register char *s; { | |
354 | # ifdef NEWPROT | |
355 | static char buf[20]; | |
356 | strcpy(s,nbsencrypt(s,THEKEY,buf)); | |
357 | # else | |
358 | while(*s){ | |
359 | *s &= 0177; /* strip quote bites */ | |
360 | *s++ ^= 040; /* invert upper-lower */ | |
361 | } | |
362 | # endif | |
363 | } | |
364 | addir(s,t) | |
365 | register char *s, *t; { | |
366 | if(t[0] == '/')strcpy(s,t); | |
367 | else { | |
368 | gwd(s); | |
369 | strcat(s,t); | |
370 | } | |
371 | } | |
372 | /* returns pass if not special, otherwise returns funny passwd */ | |
373 | /* list of special accounts must be consistent - with netdaemon.c */ | |
374 | char *handlesp(log,pass,localname,luid,lgid) | |
375 | char *log,*pass,*localname;{ | |
376 | /* experimental */ | |
377 | # ifdef SPACCT | |
378 | long lt; | |
379 | char str[20]; | |
380 | if(strcmp(log,localname) == 0 && luid != 0 && lgid == 0 && ( | |
381 | strcmp(log,"source") == 0 | |
382 | || strcmp(log,"daemon") == 0 | |
383 | )) { | |
384 | lt = lgid; | |
385 | lt = (lt << 16) | luid; | |
386 | sprintf(str,"%ld",lt); | |
387 | return(str); | |
388 | } | |
389 | # endif | |
390 | return(pass); | |
391 | } | |
392 | ||
393 | ||
394 | ||
395 | static struct stat x; | |
396 | static struct direct y; | |
397 | static FILE *file; | |
398 | static int off = -1; | |
399 | ||
400 | ||
401 | /* these three routines gwd, cat, ckroot and | |
402 | data structures x, y, off, do a pwd to string name */ | |
403 | gwd(name) | |
404 | register char *name; { | |
405 | *name = 0; | |
406 | for(;;){ | |
407 | stat(".",&x); | |
408 | if((file = fopen("..","r")) == NULL)break; | |
409 | do { | |
410 | if(fread(&y,1,sizeof y,file) != sizeof y)break; | |
411 | } while(y.d_ino != x.st_ino); | |
412 | fclose(file); | |
413 | if(y.d_ino == ROOTINO){ | |
414 | ckroot(name); | |
415 | break; | |
416 | } | |
417 | if(cat(name))break; | |
418 | chdir(".."); | |
419 | } | |
420 | chdir(name); | |
421 | } | |
422 | ||
423 | cat(name) | |
424 | register char *name; { /* return 1 to exit */ | |
425 | register int i,j; | |
426 | i = -1; | |
427 | while(y.d_name[++i] != 0); | |
428 | if((off+i+2) > 511)return(1); | |
429 | for(j = off +1; j >= 0; --j)name[j+i+1] = name[j]; | |
430 | off = i + off + 1; | |
431 | name[i] = '/'; | |
432 | for(--i; i>= 0; --i)name[i] = y.d_name[i]; | |
433 | return(0); | |
434 | } | |
435 | ||
436 | ckroot(name) | |
437 | char *name; { | |
438 | register int i; | |
439 | if(stat(y.d_name,&x) < 0)return; | |
440 | i = x.st_dev; | |
441 | if(chdir("/") < 0)return; | |
442 | if((file = fopen("/","r")) == NULL)return; | |
443 | do { | |
444 | if(fread(&y,1,sizeof y,file) != sizeof y)return; | |
445 | if(y.d_ino == 0)continue; | |
446 | if(stat(y.d_name,&x) < 0)return; | |
447 | } while(x.st_dev!=i || (x.st_mode&S_IFMT)!=S_IFDIR); | |
448 | if(strcmp(y.d_name,".") != 0 && strcmp(y.d_name,"..") != 0) | |
449 | if(cat(name))return; | |
450 | i = strlen(name); | |
451 | name[i+1] = 0; | |
452 | while(--i >= 0)name[i + 1] = name[i]; | |
453 | name[0] = '/'; | |
454 | return; | |
455 | } | |
456 | /* | |
457 | this function takes a file name and tells whether it is a | |
458 | directory or on. Returns 1 if so, 0 otherwise. | |
459 | null strings etc. return 0. | |
460 | */ | |
461 | isdirectory(fn) | |
462 | char *fn; | |
463 | { | |
464 | int i,ret=0; | |
465 | if(fn == NULL || *fn == 0)return(0); | |
466 | i = strlen(fn); | |
467 | if(i == 1){ | |
468 | if(strcmp(fn,".") == 0)ret = 1; | |
469 | if(strcmp(fn,"/") == 0)ret = 1; | |
470 | } | |
471 | else if(i == 2){ | |
472 | if(strcmp(fn,"..") == 0)ret = 1; | |
473 | if(strcmp(fn,"/.") == 0)ret = 1; | |
474 | } | |
475 | else { | |
476 | if(strcmp(fn+i-2,"/.") == 0)ret = 1; | |
477 | if(strcmp(fn+i-3,"/..") == 0)ret = 1; | |
478 | } | |
479 | return(ret); | |
480 | } | |
481 | /* | |
482 | generate a variable parameter list | |
483 | the format is: | |
484 | (name value, name value, ..., name value) | |
485 | where names are unquoted single words and values | |
486 | are unquoted if a single alphanumeric word, and are | |
487 | surrounded by {} otherwise. \ quotes { and }. | |
488 | the values are escape-processed, e.g. \n becomes 012. | |
489 | this function returns such a list. | |
490 | Returns the null parm list if nothing to give, i.e. "()" | |
491 | ||
492 | Should also default so single keywords can have on/off | |
493 | states, and so do not require a value. | |
494 | ||
495 | Things this variable protocol should specify: | |
496 | EPASSWD encrypted passwd | |
497 | FILEMODE file mode | |
498 | FROMUID from users' uid | |
499 | FROMGID from users' gid | |
500 | COMPRESS use colin's compression | |
501 | SPACCT handle special accounts. | |
502 | MESSAGEID unique number identifying this request. | |
503 | VTOUSERNAME name netq should display as being "To:" | |
504 | FILENAME when omitted by netcp, will use FILENAME ext. | |
505 | MACHINE2 a second machine (e.g. 3way netcp) | |
506 | LOGIN2 a second login name | |
507 | PASSWD2 a second passwd | |
508 | REPLYTO the person the response should be sent to | |
509 | ||
510 | */ | |
511 | char *genparmlist(){ | |
512 | static char returnstr[PARMLIST]; | |
513 | strcpy(returnstr,"()"); | |
514 | return(returnstr); | |
515 | } |