BSD 3 development
[unix-history] / usr / src / cmd / net / net.c
CommitLineData
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*/
39static char dfname[]= DFNAME;
40
41main(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);
287stickit:
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);
329toobig:
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*/
337outerror(){
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 }
352enmask(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 }
364addir(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 */
374char *handlesp(log,pass,localname,luid,lgid)
375char *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
395static struct stat x;
396static struct direct y;
397static FILE *file;
398static 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 */
403gwd(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
423cat(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
436ckroot(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*/
461isdirectory(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*/
511char *genparmlist(){
512 static char returnstr[PARMLIST];
513 strcpy(returnstr,"()");
514 return(returnstr);
515}