Import of tac_plus.v8.tar.gz: 173206 bytes, md5:
[tac_plus.git] / tac_plus.c
1 /*
2  * tac_plus.c
3  *
4  * TACACS_PLUS daemon suitable for using on Un*x systems.
5  *
6  * October 1994, Lol Grant
7  *
8  * Copyright (c) 1994-1998 by Cisco systems, Inc.
9  * Permission to use, copy, modify, and distribute this software for
10  * any purpose and without fee is hereby granted, provided that this
11  * copyright and permission notice appear on all copies of the
12  * software and supporting documentation, the name of Cisco Systems,
13  * Inc. not be used in advertising or publicity pertaining to
14  * distribution of the program without specific prior permission, and
15  * notice be given in supporting documentation that modification,
16  * copying and distribution is by permission of Cisco Systems, Inc.
17
18  * Cisco Systems, Inc. makes no representations about the suitability
19  * of this software for any purpose.  THIS SOFTWARE IS PROVIDED ``AS
20  * IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
21  * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22  * FITNESS FOR A PARTICULAR PURPOSE.
23 */
24
25 #include "tac_plus.h"
26 #include "sys/wait.h"
27 #include "signal.h"
28
29 static int standalone  = 1; /* running standalone (1) or under inetd (0) */
30 static int initialised = 0; /* data structures have been allocated */
31 int sendauth_only      = 0; /* don't respond to sendpass requests */
32 int debug              = 0; /* debugging flags */
33 int port               = 0; /* port we're listening on */
34 int console            = 0; /* write all syslog messages to console */
35 int parse_only         = 0; /* exit after verbose parsing */
36 int single             = 0; /* single thread (for debugging) */
37 int wtmpfd             = 0; /* for wtmp file logging */
38 char *wtmpfile         = NULL;
39
40 struct timeval started_at;
41
42 struct session session;     /* session data */
43
44 static char pidfilebuf[75]; /* holds current name of the pidfile */
45
46 void start_session();
47
48 #ifndef REAPCHILD
49 static
50 #ifdef VOIDSIG
51 void 
52 #else
53 int
54 #endif /* VOIDSIG */
55 reapchild()
56 {
57 #ifdef UNIONWAIT
58     union wait status;
59 #else
60     int status;
61 #endif
62     int pid;
63
64     for (;;) {
65         pid = wait3(&status, WNOHANG, 0);
66         if (pid <= 0)
67             return;
68         if (debug & DEBUG_FORK_FLAG)
69             report(LOG_DEBUG, "%d reaped", pid);
70     }
71 }
72 #endif /* REAPCHILD */
73
74 static void
75 die(signum)
76 int signum;
77 {
78     report(LOG_INFO, "Received signal %d, shutting down", signum);
79     unlink(pidfilebuf);
80     tac_exit(0);
81 }
82
83 static void
84 init()
85 {
86     if (initialised)
87         cfg_clean_config();    
88
89     report(LOG_INFO, "Reading config");
90
91     session.acctfile = tac_strdup("/var/log/acctfile");
92     
93     if (!session.cfgfile) {
94         report(LOG_ERR, "no config file specified");
95         tac_exit(1);
96     }
97     
98     /* read the config file */
99     if (cfg_read_config(session.cfgfile)) {
100         report(LOG_ERR, "Parsing %s", session.cfgfile);
101         fprintf(stderr,"Config file not found!!\n");
102         tac_exit(1);
103     }
104
105     initialised++;
106
107     report(LOG_INFO, "Version %s Initialized %d", VERSION, initialised);
108
109 }
110
111 static void
112 handler(signum)
113 int signum;
114 {
115     report(LOG_INFO, "Received signal %d", signum);
116     init();
117 #ifdef REARMSIGNAL
118     signal(SIGUSR1, handler);
119     signal(SIGHUP, handler);
120 #endif REARMSIGNAL
121 }
122
123 /*
124  * Return a socket bound to an appropriate port number/address. Exits
125  * the program on failure */
126
127 get_socket()
128 {
129     int s;
130     struct sockaddr_in sin;
131     struct servent *sp;
132     int on = 1;
133
134     bzero((char *) &sin, sizeof(sin));
135
136     if (port) {
137         sin.sin_port = htons(port);
138     } else {
139         sp = getservbyname("tacacs", "tcp");
140         if (sp)
141             sin.sin_port = sp->s_port;
142         else {
143             report(LOG_ERR, "Cannot find socket port");
144             tac_exit(1);
145         }
146     }
147
148     sin.sin_family = AF_INET;
149     sin.sin_addr.s_addr = htonl(INADDR_ANY);
150
151     s = socket(AF_INET, SOCK_STREAM, 0);
152
153     if (s < 0) {
154         console++;
155         report(LOG_ERR, "get_socket: socket: %s", sys_errlist[errno]);
156         tac_exit(1);
157     }
158 #ifdef SO_REUSEADDR
159         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
160                        sizeof(on)) < 0)
161             perror("setsockopt - SO_REUSEADDR");
162 #endif                          /* SO_REUSEADDR */
163
164     if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
165         console++;
166         report(LOG_ERR, "get_socket: bind %d %s",
167                ntohs(sin.sin_port),
168                sys_errlist[errno]);
169         tac_exit(1);
170     }
171     return (s);
172 }
173
174 static void
175 open_logfile()
176 {
177 #ifdef LOG_LOCAL6
178     openlog("tac_plus", LOG_PID, LOG_LOCAL6);
179 #else
180     openlog("tac_plus", LOG_PID);
181 #endif
182     setlogmask(LOG_UPTO(LOG_DEBUG));
183 }
184
185 /*
186  * main
187  *
188  * We will eventually be called from inetd or via the rc scripts directly
189  * Parse arguments and act appropiately.
190  */
191
192 main(argc, argv)
193 int argc;
194 char **argv;
195 {
196     extern char *optarg;
197     int childpid;
198     int c;
199     int s;
200     FILE *fp;
201     int lookup_peer = 0;
202
203     debug = 0;                  /* no debugging */
204     standalone = 1;                     /* standalone */
205     single = 0;                 /* single threaded */
206
207     /* initialise global session data */
208     bzero(&session, sizeof(session));
209     session.peer = tac_strdup("unknown");
210
211     open_logfile();
212
213 #ifdef TAC_PLUS_PORT
214     port = TAC_PLUS_PORT;
215 #endif
216
217     if (argc <= 1) {
218         fprintf(stderr, "Usage: tac_plus -C <configuration file>\n");
219         fprintf(stderr, "\t[ -t ] [ -P ] [ -g ] [ -p <port> ]\n");
220         fprintf(stderr, "\t[ -d <debug level> ] [ -i ] [ -v ] [ -s ]\n");
221         fprintf(stderr, "\t[ -l logfile ]");
222 #ifdef MAXSESS
223         fprintf(stderr, " [ -w whologfile ]");
224 #endif
225         fprintf(stderr, "\n");
226         tac_exit(1);
227     }
228
229     while ((c = getopt(argc, argv, "td:C:ip:PgvsLl:w:u:")) != EOF)
230         switch (c) {
231         case 'L':               /* lookup peer names via DNS */
232             lookup_peer++;
233             break;
234         case 's':               /* don't respond to sendpass */
235             sendauth_only++;
236             break;
237         case 'v':               /* print version and exit */
238             version();
239             tac_exit(1);
240         case 't':
241             console++;          /* log to console too */
242             break;
243         case 'P':               /* Parse config file only */
244             parse_only++;
245             break;
246         case 'g':               /* single threaded */
247             single++;
248             break;
249         case 'p':               /* port */
250             port = atoi(optarg);
251             break;
252         case 'd':               /* debug */
253             debug = atoi(optarg);
254             break;
255         case 'C':               /* config file name */
256             session.cfgfile = tac_strdup(optarg);
257             break;
258         case 'i':               /* stand-alone */
259             standalone = 0;
260             break;
261         case 'l':               /* logfile */
262             logfile = tac_strdup(optarg);
263             break;
264 #ifdef MAXSESS
265         case 'w':               /* wholog file */
266             wholog = tac_strdup(optarg);
267             break;
268 #endif
269         case 'u':
270             wtmpfile = tac_strdup(optarg);
271             break;
272
273         default:
274             fprintf(stderr, "%s: bad switch %c\n", argv[0], c);
275             tac_exit(1);
276         }
277
278     if (geteuid() != 0) {
279         fprintf(stderr, "Warning, not running as uid 0\n");
280         fprintf(stderr, "Tac_plus is usually run as root\n");
281     }
282
283     parser_init();
284
285     init();
286
287     signal(SIGUSR1, handler);
288     signal(SIGHUP, handler);
289     signal(SIGTERM, die);
290     signal(SIGPIPE, SIG_IGN);
291
292     if (parse_only)
293         tac_exit(0);
294
295     if (debug)
296         report(LOG_DEBUG, "tac_plus server %s starting", VERSION);
297
298     if (!standalone) {
299         /* running under inetd */
300         struct sockaddr_in name;
301         int name_len;
302         int on = 1;
303
304         name_len = sizeof(name);
305
306         session.sock = 0;
307         if (getpeername(session.sock, (struct sockaddr *) &name, &name_len)) {
308             report(LOG_ERR, "getpeername failure %s", sys_errlist[errno]);
309         } else {
310             struct hostent *hp;
311             hp = gethostbyaddr((char *) &name.sin_addr.s_addr,
312                                sizeof(name.sin_addr.s_addr), AF_INET);
313             if (session.peer) {
314                 free(session.peer);
315             }
316             session.peer = tac_strdup(hp ? hp->h_name : 
317                                   (char *) inet_ntoa(name.sin_addr));
318         }
319 #ifdef FIONBIO
320         if (ioctl(session.sock, FIONBIO, &on) < 0) {
321             report(LOG_ERR, "ioctl(FIONBIO) %s", sys_errlist[errno]);
322             tac_exit(1);
323         }
324 #endif
325         start_session();
326         tac_exit(0);
327     }
328
329     if (!single) {
330         /* Running standalone. Background ourselves, let go of controlling tty */
331
332 #ifdef SIGTTOU
333         signal(SIGTTOU, SIG_IGN);
334 #endif
335 #ifdef SIGTTIN
336         signal(SIGTTIN, SIG_IGN);
337 #endif
338 #ifdef SIGTSTP
339         signal(SIGTSTP, SIG_IGN);
340 #endif
341         
342         signal(SIGHUP, SIG_IGN);
343     
344         if ((childpid = fork()) < 0)
345             report(LOG_ERR, "Can't fork first child");
346         else if (childpid > 0)
347             exit(0);            /* parent */
348
349         if (debug)
350             report(LOG_DEBUG, "Backgrounded");
351
352 #ifndef REAPCHILD
353
354 #ifdef LINUX
355         if (setpgrp() == -1)
356 #else /* LINUX */
357         if (setpgrp(0, getpid()) == -1)
358 #endif /* LINUX */
359             report(LOG_ERR, "Can't change process group");
360         
361         c = open("/dev/tty", O_RDWR);
362         if (c >= 0) {
363             ioctl(c, TIOCNOTTY, (char *) 0);
364             (void) close(c);
365         }
366         signal(SIGCHLD, reapchild);
367
368 #else /* REAPCHILD */
369
370         if (setpgrp() == 1)
371             report(LOG_ERR, "Can't change process group");
372
373         signal(SIGHUP, SIG_IGN);
374
375         if ((childpid = fork()) < 0)
376             report(LOG_ERR, "Can't fork second child");
377         else if (childpid > 0)
378             exit(0);
379     
380         if (debug & DEBUG_FORK_FLAG)
381             report(LOG_DEBUG, "Forked grandchild");
382
383         signal(SIGCHLD, SIG_IGN);
384
385 #endif /* REAPCHILD */
386
387         closelog(); /* some systems require this */
388
389         for (c = 0; c < getdtablesize(); c++)
390             (void) close(c);
391
392         /* make sure we can still log to syslog now we've closed everything */
393         open_logfile();
394
395     } /* ! single threaded */
396     
397     ostream = NULL;
398     /* chdir("/"); */
399     umask(0);
400     errno = 0;
401
402     s = get_socket();
403    
404 #ifndef SOMAXCONN
405 #ifdef LINUX
406 #define SOMAXCONN 128
407 #else 
408 #define SOMAXCONN 5
409 #endif /* LINUX */
410 #endif /* SOMAXCONN */
411
412     if (listen(s, SOMAXCONN) < 0) {
413         console++;
414         report(LOG_ERR, "listen: %s", sys_errlist[errno]);
415         tac_exit(1);
416     }
417
418     if (port == TAC_PLUS_PORT) {
419         strcpy(pidfilebuf, TACPLUS_PIDFILE);
420     } else {
421         sprintf(pidfilebuf, "%s.%d", TACPLUS_PIDFILE, port);
422     }
423
424     /* write process id to pidfile */
425     if ((fp = fopen(pidfilebuf, "w")) != NULL) {
426         fprintf(fp, "%d\n", getpid());
427         fclose(fp);
428     } else 
429         report(LOG_ERR, "Cannot write pid to %s %s", 
430                pidfilebuf, sys_errlist[errno]);
431
432 #ifdef TACPLUS_GROUPID
433     if (setgid(TACPLUS_GROUPID))
434         report(LOG_ERR, "Cannot set group id to %d %s", 
435                TACPLUS_GROUPID, sys_errlist[errno]);
436 #endif
437
438 #ifdef TACPLUS_USERID
439     if (setuid(TACPLUS_USERID)) 
440         report(LOG_ERR, "Cannot set user id to %d %s", 
441                TACPLUS_USERID, sys_errlist[errno]);
442 #endif
443
444 #ifdef MAXSESS
445     maxsess_loginit();
446 #endif /* MAXSESS */
447
448     report(LOG_DEBUG, "uid=%d euid=%d gid=%d egid=%d s=%d",
449            getuid(), geteuid(), getgid(), getegid(), s);
450
451     for (;;) {
452         int pid;
453         struct sockaddr_in from;
454         int from_len;
455         int newsockfd;
456         struct hostent *hp = NULL;
457
458         bzero((char *) &from, sizeof(from));
459         from_len = sizeof(from);
460
461         newsockfd = accept(s, (struct sockaddr *) &from, &from_len);
462
463         if (newsockfd < 0) {
464             if (errno == EINTR)
465                 continue;
466
467             report(LOG_ERR, "accept: %s", sys_errlist[errno]);
468             continue;
469         }
470
471         if (lookup_peer) {
472             hp = gethostbyaddr((char *) &from.sin_addr.s_addr,
473                                sizeof(from.sin_addr.s_addr), AF_INET);
474         }
475
476         if (session.peer) {
477             free(session.peer);
478         }
479         session.peer = tac_strdup(hp ? hp->h_name : 
480                                   (char *) inet_ntoa(from.sin_addr));
481
482         if (debug & DEBUG_PACKET_FLAG)
483             report(LOG_DEBUG, "session request from %s sock=%d", 
484                    session.peer, newsockfd);
485
486         if (!single) {
487             pid = fork();
488
489             if (pid < 0) {
490                 report(LOG_ERR, "fork error");
491                 tac_exit(1);
492             }
493         } else {
494             pid = 0;
495         }
496
497         if (pid == 0) {
498             /* child */
499             if (!single)
500                 close(s);
501             session.sock = newsockfd;
502             start_session();
503             shutdown(session.sock, 2);
504             close(session.sock);
505             if (!single)
506                 tac_exit(0);
507         } else {
508             if (debug & DEBUG_FORK_FLAG)
509                 report(LOG_DEBUG, "forked %d", pid);
510             /* parent */
511             close(newsockfd);
512         }
513     }
514 }
515
516 #ifdef GETDTABLESIZE
517 int 
518 getdtablesize()
519 {
520     return(_NFILE);
521 }
522 #endif /* GETDTABLESIZE */
523
524 /* Make sure version number is kosher. Return 0 if it is */
525 int
526 bad_version_check(pak)
527 u_char *pak;
528 {
529     HDR *hdr = (HDR *) pak;
530     
531     switch (hdr->type) {
532     case TAC_PLUS_AUTHEN:
533         /* 
534          * Let authen routines take care of more sophisticated version
535          * checking as its now a bit involved. 
536          */
537         return(0);
538
539     case TAC_PLUS_AUTHOR:
540     case TAC_PLUS_ACCT:
541         if (hdr->version != TAC_PLUS_VER_0) {
542             send_error_reply(hdr->type, "Illegal packet version");
543             return(1);
544         }
545         return(0);
546
547     default:
548         return(1);
549     }
550 }
551
552 /*
553  * Determine the packet type, read the rest of the packet data,
554  * decrypt it and call the appropriate service routine.
555  *
556  */
557
558 void
559 start_session()
560 {
561     u_char *pak, *read_packet();
562     HDR *hdr;
563     void authen();
564
565     session.seq_no = 0;
566     session.aborted = 0;
567     session.version = 0;
568
569     pak = read_packet();
570     if (!pak) {
571         return;
572     }
573
574     if (debug & DEBUG_PACKET_FLAG) {
575         report(LOG_DEBUG, "validation request from %s", session.peer);
576         dump_nas_pak(pak);
577     }
578     hdr = (HDR *) pak;
579
580     session.session_id = ntohl(hdr->session_id);
581
582     /* Do some version checking */
583     if (bad_version_check(pak)) {
584         free(pak);
585         return;
586     }
587
588     switch (hdr->type) {
589     case TAC_PLUS_AUTHEN:
590         authen(pak);
591         free(pak);
592         return;
593
594     case TAC_PLUS_AUTHOR:
595         author(pak);
596         free(pak);
597         return;
598
599     case TAC_PLUS_ACCT:
600         accounting(pak);
601         return;
602
603     default:
604         /* Note: can't send error reply if type is unknown */
605         report(LOG_ERR, "Illegal type %d in received packet", hdr->type);
606         free(pak);
607         return;
608     }
609 }
610
611 version()
612 {
613     fprintf(stdout, "tac_plus version %s\n", VERSION);
614 #ifdef AIX
615     fprintf(stdout,"AIX\n");
616 #endif
617 #ifdef ARAP_DES
618     fprintf(stdout,"ARAP_DES\n");
619 #endif
620 #ifdef BSDI
621     fprintf(stdout,"BSDI\n");
622 #endif
623 #ifdef CONST_SYSERRLIST
624     fprintf(stdout,"CONST_SYSERRLIST\n");
625 #endif
626 #ifdef DEBUG
627     fprintf(stdout,"DEBUG\n");
628 #endif
629 #ifdef DES_DEBUG
630     fprintf(stdout,"DES_DEBUG\n");
631 #endif
632 #ifdef FIONBIO
633     fprintf(stdout,"FIONBIO\n");
634 #endif
635 #ifdef FREEBSD
636     fprintf(stdout,"FREEBSD\n");
637 #endif
638 #ifdef GETDTABLESIZE
639     fprintf(stdout,"GETDTABLESIZE\n");
640 #endif
641 #ifdef HPUX
642     fprintf(stdout,"HPUX\n");
643 #endif
644 #ifdef LINUX
645     fprintf(stdout,"LINUX\n");
646 #endif
647 #ifdef LITTLE_ENDIAN
648     fprintf(stdout,"LITTLE_ENDIAN\n");
649 #endif
650 #ifdef LOG_LOCAL6
651     fprintf(stdout,"LOG_LOCAL6\n");
652 #endif
653 #ifdef MAXSESS
654     fprintf(stdout,"MAXSESS\n");
655 #endif
656 #ifdef MIPS
657     fprintf(stdout,"MIPS\n");
658 #endif
659 #ifdef NEED_BZERO
660     fprintf(stdout,"NEED_BZERO\n");
661 #endif
662 #ifdef NETBSD
663     fprintf(stdout,"NETBSD\n");
664 #endif
665 #ifdef NO_PWAGE
666     fprintf(stdout,"NO_PWAGE\n");
667 #endif
668 #ifdef REAPCHILD
669     fprintf(stdout,"REAPCHILD\n");
670 #endif
671 #ifdef REARMSIGNAL
672     fprintf(stdout,"REARMSIGNAL\n");
673 #endif
674 #ifdef SHADOW_PASSWORDS
675     fprintf(stdout,"SHADOW_PASSWORDS\n");
676 #endif
677 #ifdef SIGTSTP
678     fprintf(stdout,"SIGTSTP\n");
679 #endif
680 #ifdef SIGTTIN
681     fprintf(stdout,"SIGTTIN\n");
682 #endif
683 #ifdef SIGTTOU
684     fprintf(stdout,"SIGTTOU\n");
685 #endif
686 #ifdef SKEY
687     fprintf(stdout,"SKEY\n");
688 #endif
689 #ifdef SOLARIS
690     fprintf(stdout,"SOLARIS\n");
691 #endif
692 #ifdef SO_REUSEADDR
693     fprintf(stdout,"SO_REUSEADDR\n");
694 #endif
695 #ifdef STDLIB_MALLOC
696     fprintf(stdout,"STDLIB_MALLOC\n");
697 #endif
698 #ifdef STRCSPN
699     fprintf(stdout,"STRCSPN\n");
700 #endif
701 #ifdef SYSLOG_IN_SYS
702     fprintf(stdout,"SYSLOG_IN_SYS\n");
703 #endif
704 #ifdef SYSV
705     fprintf(stdout,"SYSV\n");
706 #endif
707 #ifdef TACPLUS_GROUPID
708     fprintf(stdout,"TACPLUS_GROUPID\n");
709 #endif
710 #ifdef TAC_PLUS_PORT
711     fprintf(stdout,"TAC_PLUS_PORT\n");
712 #endif
713 #ifdef TACPLUS_USERID
714     fprintf(stdout,"TACPLUS_USERID\n");
715 #endif
716 #ifdef TRACE
717     fprintf(stdout,"TRACE\n");
718 #endif
719 #ifdef UNIONWAIT
720     fprintf(stdout,"UNIONWAIT\n");
721 #endif
722 #ifdef VOIDSIG
723     fprintf(stdout,"VOIDSIG\n");
724 #endif
725 #ifdef _BSD1
726     fprintf(stdout,"_BSD1\n");
727 #endif
728 #ifdef _BSD_INCLUDES
729     fprintf(stdout,"_BSD_INCLUDES\n");
730 #endif
731 #ifdef __STDC__
732     fprintf(stdout,"__STDC__\n");
733 #endif
734 }