1 #ident "$Id: faxq-helper.c,v 4.16 2005/04/10 20:47:43 gert Exp $ Copyright (c) Gert Doering"
2
3 /* faxq-helper.c
4 *
5 * this is a suid helper process that is used for the unprivileged
6 * fax queue client programs (faxspool, faxq, faxrm) to access the
7 * /var/spool/fax/outgoing/... fax queue ($OUT).
8 *
9 * it is NOT suid "root" but suid "FAX_OUT_USER" (usually "fax") as
10 * defined in the top level mgetty Makefile
11 *
12 * there are 5 commands:
13 *
14 * faxq-helper new
15 * user permission check (fax.allow + fax.deny)
16 * return a new job ID (F000123) and create "$OUT/.inF000123.$uid/"
17 *
18 * faxq-helper input $ID $filename
19 * validate $filename
20 * open $OUT/.in$ID.uid/$filename (O_EXCL)
21 * copy stdin to file
22 *
23 * faxq-helper activate $ID
24 * take prototype JOB file from stdin,
25 * check that "pages ..." does not reference any non-local
26 * or non-existing files
27 * check that "user ..." contains the correct value
28 * create JOB
29 * move $OUT/.inF000134.$uid/ to $OUT/F000134/ -> faxrunq will "see" it
30 *
31 * faxq-helper remove $ID
32 * check that $OUT/$ID/ exists and belongs to the calling user
33 * lock JOB
34 * rm -r $OUT/$ID/ directory tree
35 *
36 * faxq-helper requeue $ID
37 * check that $OUT/$ID/ exists and belongs to the calling user
38 * move $JOB.error to $JOB to reactivate job
39 * touch $queue_changed
40 *
41 * some checks are done globally for all commands
42 * faxq-helper must be suid fax, and fax user must exist in passwd
43 * $FAX_SPOOL_OUT must exist
44 * if $OUT is world- or group-writeable, or not owned by 'fax', issue
45 * warning (but go on) - this could be legal, but it's off-spec
46 *
47 * Note: right now, this needs an ANSI C compiler, and might not be
48 * as portable as the remaining mgetty code. Send diffs :-)
49 *
50 * $Log: faxq-helper.c,v $
51 * Revision 4.16 2005/04/10 20:47:43 gert
52 * make do_sanitize() work on a copy of the input line
53 * (strtok() modifies the input string, leading to corrupt JOB files)
54 *
55 */
56
57 #include <stdio.h>
58 #include <unistd.h>
59 #include <stdlib.h>
60 #include <ctype.h>
61 #include <errno.h>
62 #include <string.h>
63 #include <pwd.h>
64 #include <fcntl.h>
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <time.h>
68 #include <dirent.h>
69
70 #include <stdarg.h>
71
72 /* globals used by all the routines */
73 char * program_name;
74 int real_user_id; /* numeric user ID of caller */
75 char * real_user_name; /* user name of caller */
76
77 int fax_out_uid; /* user ID to chown() fax jobs to */
78 int fax_out_gid; /* group ID ... */
79
80 #define ROOT_UID 0 /* root's user ID - override checks */
81
82 #define FAX_SEQ_FILE ".Sequence"
83 #define FAX_SEQ_LOCK "LCK..seq"
84
85 #define MAXJIDLEN 20 /* maximum length of acceptable job ID */
86
87 #ifndef MAXPATHLEN
88 # define MAXPATHLEN 2048
89 #endif
90
error_and_exit(char * string)91 void error_and_exit( char * string )
92 {
93 fprintf( stderr, "%s: %s\n", program_name, string );
94 exit(1);
95 }
96
97 /* generic error messager - just to increase readability of the code below
98 */
eout(const char * fmt,...)99 void eout(const char *fmt, ...)
100 {
101 va_list ap;
102 va_start(ap, fmt);
103 fprintf( stderr, "%s: ", program_name );
104 vfprintf( stderr, fmt, ap);
105 va_end(ap);
106 }
107
108 /* validate format of job id: "Fnnnnnnn"
109 */
validate_job_id(char * JobID)110 int validate_job_id( char * JobID )
111 {
112 int rc = 0;
113 char * p = JobID;
114
115 if ( *p++ != 'F' ) rc = -1;
116 while( *p != '\0' && rc == 0 )
117 {
118 if ( ! isdigit( *p ) ) rc = -1;
119 p++;
120 }
121 if ( strlen( JobID ) > MAXJIDLEN ) rc = -1;
122
123 if ( rc<0 ) eout( "invalid JobID: '%s'\n", JobID );
124
125 return rc;
126 }
127
128 /* verify that a given path name is a directory, and is owned by "fax"
129 */
validate_dir(char * dir)130 int validate_dir( char * dir )
131 {
132 struct stat stb;
133
134 if ( lstat( dir, &stb ) < 0 )
135 {
136 eout( "can't stat '%s': %s\n", dir, strerror(errno) );
137 return -1;
138 }
139 if ( !S_ISDIR( stb.st_mode ) )
140 {
141 eout( "%s is no directory!\n", dir );
142 return -1;
143 }
144 if ( stb.st_uid != fax_out_uid )
145 {
146 eout( "job directory '%s' is not owned by '%s' (%d), abort\n",
147 dir, FAX_OUT_USER, stb.st_uid );
148 return -1;
149 }
150 return 0;
151 }
152
153 /* lookup user name in ASCII file
154 *
155 * each line consists of a single user name only
156 * if a line is too long (>100 characters) it's silently ignored because
157 * it wouldn't match anyway
158 *
159 * return values: -1 = no such file / error, 0 = not found in file, 1 = found
160 *
161 * TODO: "man faxspool" claims that "user<blank>otherstuff" is fine
162 * -> either adapt code, or rework documentation
163 */
164
find_user_in_file(char * file,char * u)165 int find_user_in_file( char * file, char * u )
166 {
167 FILE * fp;
168 char buf[100];
169
170 fp = fopen( file, "r" );
171 if ( fp == NULL )
172 {
173 if ( errno != ENOENT )
174 eout( "can't open '%s' for reading: %s\n", file, strerror(errno));
175 return -1;
176 }
177
178 while( fgets( buf, sizeof(buf)-1, fp ) != NULL )
179 {
180 int l = strlen(buf);
181
182 /* lines that are too long are just ignored */
183 if ( l>0 && buf[l-1] == '\n' )
184 {
185 buf[l-1]='\0';
186 if ( strcmp( buf, u ) == 0 )
187 { fclose( fp ); return 1; }
188 }
189 }
190
191 fclose(fp);
192 return 0;
193 }
194
195 /* check whether user may use fax service
196 * - if fax.allow exists, and user is listed -> OK
197 * - if fax.deny exists, and user is not listed -> OK
198 * - if neither exists, and user is root -> OK
199 */
check_fax_allow_deny(char * u)200 int check_fax_allow_deny( char * u )
201 {
202 int rc;
203
204 rc = find_user_in_file( FAX_ALLOW, u );
205 if ( rc == 1 ) return 0;
206 if ( rc == 0 )
207 {
208 eout( "Sorry, %s, you are not allowed to use the fax service.\n", u );
209 return -1;
210 }
211
212 rc = find_user_in_file( FAX_DENY, u );
213 if ( rc == 0 ) return 0;
214 if ( rc == 1 )
215 {
216 eout( "Sorry, %s, you are not allowed to use the fax service.\n", u );
217 return -1;
218 }
219
220 if ( strcmp( u, "root" ) == 0 ) return 0;
221
222 eout( "Neither fax.allow nor fax.deny exist,\n"
223 "so only 'root' may use the fax service. Sorry.\n" );
224 return -1;
225 }
226
227 /* create next sequence number
228 * - if sequence file doesn't exist -> create, and return "1"
229 * - if locks/ subdir doesn't exist, create
230 * - lock file by creating hard link
231 * - read current sequence number, add 1, write back
232 * - unlock
233 */
get_next_seq(void)234 long get_next_seq(void)
235 {
236 char buf[100];
237 int try = 0;
238 long s = -1;
239 int fd, l;
240
241 again:
242 if ( link( FAX_SEQ_FILE, FAX_SEQ_LOCK ) < 0 )
243 {
244 if ( errno == EEXIST ) /* lock exists */
245 {
246 if ( ++try < 3 )
247 {
248 eout( "sequence file locked (try %d)...\n", try );
249 sleep( rand()%3 +1 );
250 goto again;
251 }
252 eout( "can't lock sequence file, give up\n" );
253 return -1;
254 }
255
256 if ( errno == ENOENT ) /* sequence file does not exist */
257 {
258 eout( "sequence file does not exist, creating...\n" );
259 fd = creat( FAX_SEQ_FILE, 0644 );
260 if ( fd < 0 )
261 {
262 eout( "can't create sequence file '%s': %s\n",
263 FAX_SEQ_FILE, strerror(errno) );
264 return -1;
265 }
266 write( fd, "000000\n", 7 );
267 close( fd );
268 goto again;
269 }
270
271 eout( "can't lock sequence file: %s\n", strerror(errno) );
272 return -1;
273 }
274
275 /* sequence file is locked, now read current sequence */
276 fd = open( FAX_SEQ_FILE, O_RDWR );
277 if ( fd < 0 )
278 {
279 eout( "can't open '%s' read/write: %s\n",
280 FAX_SEQ_FILE, strerror(errno) );
281 goto unlock_and_out;
282 }
283
284 l = read( fd, buf, sizeof(buf)-1 );
285 if ( l >= 0 ) buf[l] = '\0';
286
287 if ( l < 0 || l >= sizeof(buf)-1 || ! isdigit( buf[0] ) )
288 {
289 eout( "sequence file '%s' corrupt\n", FAX_SEQ_FILE );
290 goto close_and_out;
291 }
292
293 s = atol( buf ) + 1;
294 sprintf( buf, "%0*ld\n", l-1, s );
295
296 if ( lseek( fd, 0, SEEK_SET ) != 0 )
297 {
298 eout( "can't rewind sequence file: %s\n", strerror(errno) );
299 goto close_and_out;
300 }
301
302 l = strlen(buf);
303 if ( write( fd, buf, l ) != l )
304 {
305 eout( "can't write all %d bytes to %s: %s\n",
306 l, FAX_SEQ_FILE, strerror(errno) );
307 }
308
309 close_and_out:
310 close(fd);
311
312 unlock_and_out:
313 unlink( FAX_SEQ_LOCK );
314 return s;
315 }
316
317 /* create a new job
318 * - check user permissions (fax.allow/fax.deny)
319 * - get next sequence number
320 * - create directory (prefixed with ".in", suffixed with user ID)
321 * - print job ID to stdout
322 */
do_new(void)323 int do_new( void )
324 {
325 long seq;
326 char dirbuf[100];
327
328 /* check if user may use fax service (fax.allow/fax.deny files) */
329 if ( check_fax_allow_deny( real_user_name ) < 0 ) return -1;
330
331 /* get next sequence number (including locking) */
332 seq = get_next_seq();
333
334 if ( seq <= 0 ) return -1;
335
336 sprintf( dirbuf, ".inF%06ld.%d", seq, real_user_id );
337
338 if ( mkdir(dirbuf, 0700) < 0 )
339 {
340 eout( "can't create directory '%s': %s\n", dirbuf, strerror(errno) );
341 return -1;
342 }
343
344 /* print file name (without ".in") to stdout */
345 printf( "F%06ld\n", seq );
346 return 0;
347 }
348
349 /* do_input
350 * validate job ID and input file name
351 * files with "/" are only allowed in one special case (.source-files/)
352 * read file from stdin, write to $OUT/.in$JID.uid/$filename
353 */
do_input(char * JID,char * outfilename)354 int do_input( char * JID, char * outfilename )
355 {
356 char * p;
357 char dir1[MAXJIDLEN+20];
358 char pathbuf[200], buf[4096];
359 int fd, r, w;
360
361 if ( isatty(fileno(stdin)) )
362 fprintf(stderr, "NOTICE: reading input from stdin, end with ctrl-D\n");
363
364 sprintf( dir1, ".in%s.%d", JID, real_user_id );
365 if ( validate_dir( dir1 ) < 0 ) return -1;
366
367 p = outfilename;
368 if ( strncmp( outfilename, ".source-files/", 14 ) == 0 )
369 {
370 p+=14;
371 sprintf( pathbuf, "%s/.source-files", dir1 );
372 if ( mkdir( pathbuf, 0755 ) < 0 && errno != EEXIST )
373 {
374 eout( "can't mkdir '%s': %s\n", pathbuf, strerror(errno));
375 return -1;
376 }
377 }
378
379 while( *p != '\0' )
380 {
381 if ( *p == '/' || *p == '\\' || isspace(*p) || !isprint(*p) )
382 {
383 eout( "invalid char. '%c' in file name '%s', abort\n",
384 *p, outfilename );
385 return -1;
386 }
387 p++;
388 }
389
390 if ( strlen( dir1 ) + strlen( outfilename ) >= sizeof(pathbuf) -3 )
391 {
392 eout( "'%s/%s': file name too long\n" ); return -1;
393 }
394
395 sprintf( pathbuf, "%s/%s", dir1, outfilename );
396
397 fd = open( pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0644 );
398 if ( fd < 0 )
399 {
400 eout( "can't open '%s' for writing: %s\n", pathbuf, strerror(errno));
401 return -1;
402 }
403
404 while( ( r = read( fileno(stdin), buf, sizeof(buf) ) ) > 0 )
405 {
406 w = write( fd, buf, r );
407 if ( w != r )
408 {
409 eout( "can't write all %d bytes to %s: %s\n",
410 r, pathbuf, strerror(errno) );
411 break;
412 }
413 }
414
415 if ( r != 0 ) /* read or write error */
416 {
417 if ( r < 0 )
418 eout( "error reading from stdin: %s\n", strerror(errno));
419 close(fd);
420 unlink(pathbuf);
421 return -1;
422 }
423
424 close(fd);
425 return 0;
426 }
427
428 /* do a "rm -rf <dir>"
429 * TODO: check for ownership?
430 */
recursive_rm(char * dir)431 int recursive_rm( char * dir )
432 {
433 char pathbuf[MAXPATHLEN];
434 DIR * dirp;
435 struct dirent * de;
436 struct stat stb;
437 int rc = 0;
438
439 if ( ( dirp = opendir( dir ) ) == NULL )
440 {
441 eout( "can't read directory '%s': %s\n", dir, strerror(errno));
442 return -1;
443 }
444
445 while( ( de = readdir( dirp ) ) != NULL )
446 {
447 if ( strcmp( de->d_name, "." ) == 0 ||
448 strcmp( de->d_name, ".." ) == 0 )
449 {
450 continue;
451 }
452 if ( strlen( dir ) + strlen( de->d_name ) > sizeof(pathbuf) -5 )
453 {
454 eout( "file path too long: %s/%s\n", dir, de->d_name );
455 rc--;
456 continue;
457 }
458 sprintf( pathbuf, "%s/%s", dir, de->d_name );
459
460 /* fprintf( stderr, "debug2: '%s'\n", pathbuf ); */
461
462 if ( lstat( pathbuf, &stb ) < 0 )
463 {
464 eout( "can't stat '%s': %s\n", pathbuf, strerror(errno));
465 rc--;
466 continue;
467 }
468
469 /* directories are followed, everything else is removed */
470 if ( S_ISDIR( stb.st_mode ) )
471 {
472 rc += recursive_rm( pathbuf );
473 }
474 else
475 {
476 if ( unlink( pathbuf ) < 0 )
477 {
478 eout( "can't unlink '%s': %s\n", pathbuf, strerror( errno ));
479 rc--;
480 }
481 }
482 }
483 closedir( dirp );
484
485 if ( rmdir( dir ) < 0 )
486 {
487 eout( "can't rmdir '%s': %s\n", dir, strerror(errno));
488 rc--;
489 }
490
491 return rc;
492 }
493
494
495 /* make sure that all path names in the following list (separated by
496 * whitespace) are local to this directory, exist, and are regular files
497 */
do_sanitize_page_files(char * dir,char * filelist)498 int do_sanitize_page_files( char * dir, char * filelist )
499 {
500 char * p, * copy, tmp[300];
501 struct stat stb;
502 int n=0;
503 int l;
504
505 l = strlen(filelist);
506 if ( l == 0 ) return 0; /* empty list is OK */
507
508 copy = malloc( l+1 );
509 if ( copy == NULL )
510 {
511 eout( "in do_sanitize: cannot malloc() %d bytes, abort\n", l ); return -1;
512 }
513 memcpy( copy, filelist, l+1 );
514
515 p = strtok( copy, " \t\n" );
516
517 while( p != NULL )
518 {
519 if ( strchr( p, '/' ) != NULL )
520 {
521 eout( "non-local file name: '%s', abort\n", p ); return -1;
522 }
523
524 if ( strlen( dir ) + strlen( p ) + 3 >= sizeof(tmp) )
525 {
526 eout( "file name '%s' too long, abort\n", p ); return -1;
527 }
528 sprintf( tmp, "%s/%s", dir, p );
529
530 if ( lstat( tmp, &stb ) < 0 )
531 {
532 eout( "can't stat file '%s': %s\n", tmp, strerror(errno) );
533 return -1;
534 }
535
536 if ( !S_ISREG( stb.st_mode ) )
537 {
538 eout( "'%s' is not a regular file, abort\n", tmp ); return -1;
539 }
540
541 n++;
542 p = strtok( NULL, " \t\n" );
543 }
544
545 return n;
546 }
547
548 /* Activate "pending" fax job
549 *
550 */
do_activate(char * JID)551 int do_activate( char * JID )
552 {
553 char dir1[MAXJIDLEN+20];
554 char buf[1000], *p, *q;
555 int fd;
556 int user_seen = 0;
557
558 if ( isatty(fileno(stdin)) )
559 fprintf(stderr, "NOTICE: reading input from stdin, end with ctrl-D\n");
560
561 sprintf( dir1, ".in%s.%d", JID, real_user_id );
562 if ( validate_dir( dir1 ) < 0 ) return -1;
563
564 sprintf( buf, "%s/JOB", dir1 );
565
566 /* the JOB file has to be world-readable, relax umask */
567 umask( 0022 );
568
569 /* TODO: check if this portably catches symlinks to non-existant files! */
570 if ( ( fd = open( buf, O_WRONLY | O_CREAT | O_EXCL, 0644 ) ) < 0 )
571 {
572 eout( "can't create JOB file '%s': %s\n", buf, strerror(errno) );
573 recursive_rm(dir1);
574 return -1;
575 }
576
577 /* read queue metadata from stdin, sanitize, write to JOB fd */
578 while( ( p = fgets( buf, sizeof(buf)-1, stdin ) ) != NULL )
579 {
580 int l = strlen(buf);
581
582 if ( l >= sizeof(buf)-2 )
583 {
584 eout( "input line too long\n" ); break;
585 }
586
587 if ( l>0 && buf[l-1] == '\n' ) buf[--l]='\0';
588
589 if ( strncmp(buf, "user ", 5) == 0 )
590 {
591 user_seen=1;
592 if ( real_user_id != ROOT_UID &&
593 strcmp( buf+5, real_user_name ) != 0 )
594 {
595 eout( "user name mismatch (%s <-> %s)\n", buf+5, real_user_name );
596 break;
597 }
598 }
599 if ( strncmp(buf, "pages", 5 ) == 0 &&
600 do_sanitize_page_files( dir1, buf+5 ) < 0 )
601 {
602 eout( "bad input files specified\n" );
603 break;
604 }
605
606 /* replace all quote characters, backslash and ';' by '_' */
607 for( q = buf; *q != '\0'; q++ )
608 {
609 if ( *q == '\'' || *q == '"' || *q == '`' ||
610 *q == '\\' || *q == ';' )
611 { *q = '_'; }
612 }
613
614 /* and write to JOB file... */
615 buf[l++] = '\n';
616 if ( write( fd, buf, l ) != l )
617 {
618 eout( "can't write line to JOB file: %s\n", strerror(errno) );
619 break;
620 }
621 }
622
623 if ( p != NULL ) /* loop aborted */
624 {
625 close(fd); recursive_rm(dir1); return -1;
626 }
627
628 if ( !user_seen ) /* no "user ..." line in JOB? */
629 {
630 sprintf( buf, "user %.100s\n", real_user_name );
631 write( fd, buf, strlen(buf) );
632 }
633 close(fd);
634
635 /* now make directory world-readable & move to final place */
636 if ( chmod( dir1, 0755 ) < 0 )
637 {
638 eout( "can't chmod '%s' to 0755: %s\n", dir1, strerror(errno));
639 recursive_rm(dir1); return -1;
640 }
641
642 if ( rename( dir1, JID ) < 0 )
643 {
644 eout( "can't rename '%s' to '%s': %s\n", dir1, JID, strerror(errno));
645 recursive_rm(dir1); return -1;
646 }
647
648 return 0;
649 }
650
651
652 /* helper function for do_remove and do_requeue
653 * - check whether /$JID/ exists at all (and is a directory)
654 * - lock $JID/$jobfile, if present
655 * - check $JID/$jobfile for "user xxx" and compare with caller uid
656 *
657 * TODO: permit "root" override
658 */
check_user_perms(char * JID,char * jobfile)659 int check_user_perms( char * JID, char * jobfile )
660 {
661 struct stat stb;
662 char buf[1000], *p;
663 FILE * fp;
664 char jfile[MAXJIDLEN+30], lfile[MAXJIDLEN+30];
665
666 if ( lstat( JID, &stb ) < 0 ||
667 !S_ISDIR( stb.st_mode ) ||
668 stb.st_uid != fax_out_uid )
669 {
670 eout( "'%s' is not a directory or has wrong owner\n", JID );
671 return -1;
672 }
673
674 sprintf( jfile, "%s/%s", JID, jobfile );
675 sprintf( lfile, "%s/%s", JID, "JOB.locked" );
676
677 if ( link( jfile, lfile ) < 0 )
678 {
679 if ( errno == EEXIST )
680 {
681 eout( "%s already locked\n", jfile ); return -1;
682 }
683 if ( errno == ENOENT )
684 {
685 return -2; /* signal "file not found" to caller */
686 }
687
688 eout( "can't lock JOB file: %s\n", strerror(errno) ); return -1;
689 }
690
691 if ( ( fp = fopen( jfile, "r" ) ) == NULL )
692 {
693 eout( "can't open '%s' for reading: %s\n", jfile, strerror(errno) );
694 unlink( lfile );
695 return -1;
696 }
697
698 while( ( p = fgets( buf, sizeof(buf)-1, fp ) ) != NULL )
699 {
700 int l = strlen(buf);
701
702 if ( l >= sizeof(buf)-2 )
703 {
704 eout( "input line too long\n" );
705 unlink( lfile );
706 fclose(fp);
707 return -1;
708 }
709
710 if ( l>0 && buf[l-1] == '\n' ) buf[--l]='\0';
711
712 if ( strncmp(buf, "user ", 5) == 0 )
713 {
714 if ( real_user_id != ROOT_UID &&
715 strcmp( buf+5, real_user_name ) != 0 )
716 {
717 fprintf( stderr, "%s: not your job, can't do anything (%s <-> %s)\n", jfile, buf+5, real_user_name );
718 unlink( lfile );
719 fclose(fp);
720 return -1;
721 }
722 }
723 }
724
725 fclose(fp);
726 return 0; /* lock file purposely kept in place! */
727 }
728
do_remove(char * JID)729 int do_remove( char * JID )
730 {
731 int rc;
732
733 rc = check_user_perms( JID, "JOB" );
734 if ( rc == -2 ) /* not found */
735 rc = check_user_perms( JID, "JOB.error" );
736 if ( rc == -2 ) /* not found */
737 rc = check_user_perms( JID, "JOB.suspended" );
738 if ( rc == -2 ) /* not found */
739 rc = check_user_perms( JID, "JOB.done" );
740
741 if ( rc < 0 ) /* check failed */
742 {
743 if ( rc == -2 ) /* still not found */
744 eout( "no JOB file in %s/ - can't verify permissions\n", JID );
745 return -1;
746 }
747
748 rc = recursive_rm( JID );
749
750 if ( rc < 0 )
751 {
752 eout( "could not remove %d files or subdirectories\n", -rc );
753 return -1;
754 }
755 return 0;
756 }
757
do_requeue(char * JID)758 int do_requeue( char * JID )
759 {
760 int rc, fd;
761 char file1[MAXJIDLEN+30], file2[MAXJIDLEN+30];
762 char buf[100];
763 time_t ti;
764
765 rc = check_user_perms( JID, "JOB.suspended" );
766 if ( rc == -2 )
767 {
768 eout( "no %s/JOB.suspended found, do nothing\n", JID );
769 return -1;
770 }
771 if ( rc < 0 ) { return -1; }
772
773 /* JOB.suspended found, and user permissions OK */
774
775 sprintf( file1, "%s/JOB.suspended", JID );
776 sprintf( file2, "%s/JOB", JID );
777
778 if ( ( fd = open( file1, O_WRONLY | O_APPEND ) ) < 0 )
779 {
780 eout( "can't open '%s' to append status line: %s\n",
781 file1, strerror(errno));
782 return -1;
783 }
784
785 time(&ti);
786 sprintf( buf, "Status %.40s", ctime(&ti) );
787 sprintf( &buf[strlen(buf)-1], " - reactivated by %.16s\n", real_user_name);
788
789 rc = strlen(buf);
790 if ( write( fd, buf, rc ) != rc )
791 {
792 eout( "can't write all %d bytes to '%s': %s\n",
793 rc, file1, strerror(errno) );
794 close( fd );
795 return -1;
796 }
797 close( fd );
798
799 if ( rename( file1, file2 ) < 0 )
800 {
801 eout( "can't rename '%s' to '%s': %s\n",
802 file1, file2, strerror(errno) );
803 return -1;
804 }
805
806 sprintf( file1, "%s/JOB.locked", JID );
807
808 if ( unlink( file1 ) < 0 )
809 {
810 eout( "can't unlink '%s': %s\n", file1, strerror(errno) );
811 }
812
813 /* signal to faxrunqd that queue needs re-reading */
814 fd = open( ".queue-changed", O_WRONLY | O_CREAT | O_EXCL, 0644 );
815 if ( fd < 0 )
816 {
817 if ( errno != EEXIST )
818 eout( "can't create '.queue-changed' file: %s\n", strerror(errno));
819 }
820 else
821 close(fd);
822
823 return 0;
824 }
825
main(int argc,char ** argv)826 int main( int argc, char ** argv )
827 {
828 struct passwd * pw; /* for user name */
829 struct stat stb;
830
831 program_name = strrchr( argv[0], '/' );
832 if ( program_name != NULL ) program_name++;
833 else program_name = argv[0];
834
835 if ( argc < 2 )
836 { error_and_exit( "keyword missing" ); }
837
838 /* common things to check and prepare */
839
840 /* make sure people do not play umask tricks on us - the only
841 * bits that are accepted in a user umask are "044" - permit/prevent
842 * read access by group/other. Write access is always denied.
843 */
844 umask( ( umask(0) & 0044 ) | 0022 );
845
846 /* get numeric uid/gid for fax user */
847 pw = getpwnam( FAX_OUT_USER );
848 if ( pw == NULL )
849 {
850 eout( "can't get user ID for user '%s', abort!\n", FAX_OUT_USER );
851 exit(3);
852 }
853 fax_out_uid = pw->pw_uid;
854 fax_out_gid = pw->pw_gid;
855
856 /* effective user ID is root, real user ID is still the caller's */
857 if ( geteuid() != fax_out_uid )
858 {
859 eout( "must be set-uid '%s'\n", FAX_OUT_USER );
860 exit(3);
861 }
862 real_user_id = getuid();
863 pw = getpwuid( real_user_id );
864 if ( pw == NULL || pw->pw_name == NULL )
865 {
866 eout( "you don't exist, go away (uid=%d)!\n", real_user_id );
867 exit(3);
868 }
869 real_user_name = pw->pw_name;
870
871 /* spool directory has to exist, and should be owned by 'fax' */
872 if ( chdir( FAX_SPOOL_OUT ) < 0 )
873 {
874 eout( "can't chdir to %s: %s\n", FAX_SPOOL_OUT, strerror(errno) );
875 exit(2);
876 }
877 if ( stat( ".", &stb ) < 0 )
878 {
879 eout( "can't stat %s: %s\n", FAX_SPOOL_OUT, strerror(errno) );
880 exit(2);
881 }
882 if ( ( stb.st_mode & 0022 ) > 0 )
883 {
884 eout( "WARNING: %s is group- or world-writeable\n", FAX_SPOOL_OUT);
885 }
886 if ( stb.st_uid != fax_out_uid )
887 {
888 eout( "WARNING: %s should be owned by user '%s'\n",
889 FAX_SPOOL_OUT, FAX_OUT_USER );
890 }
891
892 /* now parse arguments and go to specific functions */
893 if ( argc == 2 && strcmp( argv[1], "new" ) == 0 )
894 {
895 exit( do_new() <0? 10: 0);
896 }
897 if ( argc == 4 && strcmp( argv[1], "input" ) == 0 )
898 {
899 /* second parameter is job ID, 3rd is file name */
900 char * job_id = argv[2];
901 char * file_name = argv[3];
902 if ( validate_job_id( job_id ) <0 ) exit(1);
903
904 exit( do_input( job_id, file_name ) <0? 10: 0);
905 }
906 if ( argc == 3 )
907 {
908 /* second parameter is common for all commands: job ID */
909 char * job_id = argv[2];
910 if ( validate_job_id( job_id ) <0 ) exit(1);
911
912 if( strcmp( argv[1], "activate" ) == 0 )
913 {
914 exit( do_activate( job_id ) <0? 10: 0);
915 }
916 if ( strcmp( argv[1], "remove" ) == 0 )
917 {
918 exit( do_remove( job_id ) <0? 10: 0);
919 }
920 if ( strcmp( argv[1], "requeue" ) == 0 )
921 {
922 exit( do_requeue( job_id ) <0? 10: 0);
923 }
924 }
925
926 error_and_exit( "invalid keyword or wrong number of parameters" );
927 return 0;
928 }
929