import from HEAD:
[freeradius.git] / src / main / radwho.c
1 /*
2  * radwho.c     Show who is logged in on the terminal servers.
3  *              Can also be installed as fingerd on the UNIX
4  *              machine RADIUS runs on.
5  *
6  * Version:     $Id$
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation; either version 2 of the License, or
11  *   (at your option) any later version.
12  *
13  *   This program is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * Copyright 2000  The FreeRADIUS server project
23  * Copyright 2000  Alan DeKok <aland@ox.org>
24  */
25
26 static const char rcsid[] =
27 "$Id$";
28
29 #include "autoconf.h"
30 #include "libradius.h"
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <pwd.h>
35 #include <sys/stat.h>
36 #include <sys/utsname.h>
37 #include <ctype.h>
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
40 #endif
41
42 #include "sysutmp.h"
43 #include "radutmp.h"
44 #include "radiusd.h"
45 #include "conffile.h"
46
47 /*
48  *      FIXME: put in header file.
49  */
50 #define SYS_FINGER "/usr/bin/finger"
51 #define FINGER_DIR "/usr/local/lib/finger"
52
53 /*
54  *      Header above output and format.
55  */
56 static const char *hdr1 =
57 "Login      Name              What  TTY  When      From      Location";
58 static const char *rfmt1 = "%-10.10s %-17.17s %-5.5s %s%-3d %-9.9s %-9.9s %-.19s%s";
59 static const char *rfmt1r = "%s,%s,%s,%s%u,%s,%s,%s%s";
60
61 static const char *hdr2 =
62 "Login      Port    What      When          From       Location";
63 static const char *rfmt2 = "%-10.10s %s%-5d  %-6.6s %-13.13s %-10.10s %-.28s%s";
64 static const char *rfmt2r = "%s,%s%d,%s,%s,%s,%s%s";
65
66 static const char *eol = "\n";
67 static int showname = -1;
68 static int showptype = 0;
69 static int showcid = 0;
70 int debug_flag = 0;
71 const char *progname = "radwho";
72 const char *radlog_dir = NULL;
73 const char *radutmp_file = NULL;
74
75 const char *radius_dir = NULL;
76 const char *radacct_dir = NULL;
77 const char *radlib_dir = NULL;
78 uint32_t myip = INADDR_ANY;
79 int log_stripped_names;
80
81 radlog_dest_t radlog_dest = RADLOG_STDOUT;
82
83 /*
84  *      Global, for log.c to use.
85  */
86 struct main_config_t mainconfig;
87
88 struct radutmp_config_t {
89   char *radutmp_fn;
90 } radutmpconfig;
91
92 static const CONF_PARSER module_config[] = {
93   { "filename", PW_TYPE_STRING_PTR, 0, &radutmpconfig.radutmp_fn,  RADUTMP },
94   { NULL, -1, 0, NULL, NULL }
95 };
96
97 /*
98  *      Safe popen. Ugh.
99  */
100 static FILE *safe_popen(const char *cmd, const char *mode)
101 {
102         char            *p;
103         char            buf[1024];
104
105         /*
106          *      Change all suspect characters into a space.
107          */
108         strncpy(buf, cmd, sizeof(buf));
109         buf[sizeof(buf) - 1] = 0;
110         for (p = buf; *p; p++) {
111                 if (isalnum((int) *p))
112                         continue;
113                 if (strchr("@%-_ \t+:,./", *p) == NULL)
114                         *p = ' ';
115         }
116
117         return popen(buf, mode);
118 }
119
120 /*
121  *      Print a file from FINGER_DIR. If the file is executable,
122  *      execute it instead. Return 0 if succesfull.
123  */
124 static int ffile(const char *arg)
125 {
126         FILE *fp;
127         char fn[1024];
128         int p = 0;
129         char *s;
130
131         snprintf(fn, sizeof(fn), "%s/%.32s", FINGER_DIR, arg);
132         if (access(fn, X_OK) == 0) {
133                 p = 1;
134                 snprintf(fn, sizeof(fn), "exec %s/%.32s 2>&1", FINGER_DIR, arg);
135                 fp = safe_popen(fn, "r");
136         } else fp = fopen(fn, "r");
137
138         if (fp == NULL)
139                 return -1;
140
141         while(fgets(fn, 1024, fp)) {
142                 if ((s = strchr(fn, '\n')) != NULL)
143                         *s = 0;
144                 fprintf(stdout, "%s\r\n", fn);
145         }
146         if (p)
147                 pclose(fp);
148         else
149                 fclose(fp);
150         fflush(stdout);
151         return 0;
152 }
153
154
155 /*
156  *      Execute the system finger and translate LF to CRLF.
157  */
158 static void sys_finger(const char *l)
159 {
160         FILE *fp;
161         char fn[1024];
162         char *p;
163
164         if (ffile(l) == 0)
165                 exit(0);
166
167         snprintf(fn, sizeof(fn), "exec %s %s", SYS_FINGER, l);
168         if ((fp = safe_popen(fn, "r")) == NULL) {
169                 printf("popen: %s\r\n", strerror(errno));
170                 exit(1);
171         }
172
173         while(fgets(fn, 1024, fp)) {
174                 if ((p = strchr(fn, '\n')) != NULL)
175                         *p = 0;
176                 fprintf(stdout, "%s\r\n", fn);
177         }
178         pclose(fp);
179         exit(0);
180 }
181
182
183 /*
184  *      Get fullname of a user.
185  */
186 static char *fullname(char *username)
187 {
188         struct passwd *pwd;
189         char *s;
190
191         if ((pwd = getpwnam(username)) != NULL) {
192                 if ((s = strchr(pwd->pw_gecos, ',')) != NULL) *s = 0;
193                 return pwd->pw_gecos;
194         }
195         return username;
196 }
197
198 /*
199  *      Return protocol type.
200  */
201 static const char *proto(int id, int porttype)
202 {
203         static char buf[8];
204
205         if (showptype) {
206                 if (!strchr("ASITX", porttype))
207                         porttype = ' ';
208                 if (id == 'S')
209                         snprintf(buf, sizeof(buf), "SLP %c", porttype);
210                 else if (id == 'P')
211                         snprintf(buf, sizeof(buf), "PPP %c", porttype);
212                 else
213                         snprintf(buf, sizeof(buf), "shl %c", porttype);
214                 return buf;
215         }
216         if (id == 'S') return "SLIP";
217         if (id == 'P') return "PPP";
218         return "shell";
219 }
220
221 /*
222  *      Return a time in the form day hh:mm
223  */
224 static char *dotime(time_t t)
225 {
226         char *s = ctime(&t);
227
228         if (showname) {
229                 strncpy(s + 4, s + 11, 5);
230                 s[9] = 0;
231         } else {
232                 strncpy(s + 4, s + 8, 8);
233                 s[12] = 0;
234         }
235
236         return s;
237 }
238
239
240 /*
241  *      Print address of NAS.
242  */
243 static const char *hostname(char *buf, size_t buflen, uint32_t ipaddr)
244 {
245         if (ipaddr == 0 || ipaddr == (uint32_t)-1 || ipaddr == (uint32_t)-2)
246                 return "";
247         return ip_hostname(buf, buflen, ipaddr);
248 }
249
250
251 /*
252  *      Print usage message and exit.
253  */
254 static void NEVER_RETURNS usage(int status)
255 {
256         FILE *output = status?stderr:stdout;
257
258         fprintf(output, "Usage: radwho [-d raddb] [-cfihnprRsSZ] [-N nas] [-P nas_port] [-u user] [-U user]\n");
259         fprintf(output, "       -c: show caller ID, if available\n");
260         fprintf(output, "       -d: set the raddb directory (default is %s)\n",
261                 RADIUS_DIR);
262         fprintf(output, "       -f: give fingerd output\n");
263         fprintf(output, "       -i: show session ID\n");
264         fprintf(output, "       -n: no full name\n");
265         fprintf(output, "       -N <nas-ip-address>: Show entries matching the given NAS IP address\n");
266         fprintf(output, "       -p: show port type\n");
267         fprintf(output, "       -P <port>: Show entries matching the given nas port\n");
268         fprintf(output, "       -r: Print output as raw comma-delimited data\n");
269         fprintf(output, "       -R: Print output as RADIUS attributes and values\n");
270         fprintf(output, "           Includes ALL information from the radutmp record.\n");
271         fprintf(output, "       -s: show full name\n");
272         fprintf(output, "       -S: hide shell users from radius\n");
273         fprintf(output, "       -u <user>: Show entries matching the given user\n");
274         fprintf(output, "       -U <user>: like -u, but case-sensitive\n");
275         fprintf(output, "       -Z: Include accounting stop information in radius output.  Requires -R.\n");
276         exit(status);
277 }
278
279
280 /*
281  *      Main program, either pmwho or fingerd.
282  */
283 int main(int argc, char **argv)
284 {
285         CONF_SECTION *maincs, *cs;
286         FILE *fp;
287         struct radutmp rt;
288         char inbuf[128];
289         char othername[256];
290         char nasname[1024];
291         char session_id[sizeof(rt.session_id)+1];
292         int fingerd = 0;
293         int hideshell = 0;
294         int showsid = 0;
295         int rawoutput = 0;
296         int radiusoutput = 0;   /* Radius attributes */
297         char *p, *q;
298         const char *portind;
299         int c;
300         unsigned int portno;
301         char buffer[2048];
302         const char *user = NULL;
303         int user_cmp = 0;
304         time_t now = 0;
305         uint32_t nas_port = ~0;
306         uint32_t nas_ip_address = INADDR_NONE;
307         int zap = 0;
308
309         radius_dir = RADIUS_DIR;
310
311         while((c = getopt(argc, argv, "d:flnN:sSipP:crRu:U:Z")) != EOF) switch(c) {
312                 case 'd':
313                         radius_dir = optarg;
314                         break;
315                 case 'f':
316                         fingerd++;
317                         showname = 0;
318                         break;
319                 case 'h':
320                         usage(0);
321                         break;
322                 case 'S':
323                         hideshell = 1;
324                         break;
325                 case 'n':
326                         showname = 0;
327                         break;
328                 case 'N':
329                         nas_ip_address = ip_addr(optarg);
330                         if (nas_ip_address == INADDR_NONE) {
331                                 usage(1);
332                         }
333                         break;
334                 case 's':
335                         showname = 1;
336                         break;
337                 case 'i':
338                         showsid = 1;
339                         break;
340                 case 'p':
341                         showptype = 1;
342                         break;
343                 case 'P':
344                         nas_port = atoi(optarg);
345                         break;
346                 case 'c':
347                         showcid = 1;
348                         showname = 1;
349                         break;
350                 case 'r':
351                         rawoutput = 1;
352                         break;
353                 case 'R':
354                         radiusoutput = 1;
355                         now = time(NULL);
356                         break;
357                 case 'u':
358                         user = optarg;
359                         user_cmp = 0;
360                         break;
361                 case 'U':
362                         user = optarg;
363                         user_cmp = 1;
364                         break;
365                 case 'Z':
366                         zap = 1;
367                         break;
368                 default:
369                         usage(1);
370                         break;
371         }
372
373         /*
374          *      Be safe.
375          */
376         if (zap && !radiusoutput) zap = 0;
377
378         /*
379          *      zap EVERYONE, but only on this nas
380          */
381         if (zap && !user && (~nas_port == 0)) {
382                 /*
383                  *      We need to know which NAS to zap users in.
384                  */
385                 if (nas_ip_address == INADDR_NONE) usage(1);
386
387                 printf("Acct-Status-Type = Accounting-Off\n");
388                 printf("NAS-IP-Address = %s\n",
389                        ip_hostname(buffer, sizeof(buffer), nas_ip_address));
390                 printf("Acct-Delay-Time = 0\n");
391                 exit(0);        /* don't bother printing anything else */
392         }
393         
394         /*
395          *      Initialize mainconfig
396          */
397         memset(&mainconfig, 0, sizeof(mainconfig));
398
399         /* Read radiusd.conf */
400         snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", radius_dir);
401         maincs = conf_read(NULL, 0, buffer, NULL);
402         if (!maincs) {
403                 fprintf(stderr, "%s: Error reading radiusd.conf.\n", argv[0]);
404                 exit(1);
405         }
406
407         /* Read the radutmp section of radiusd.conf */
408         cs = cf_section_sub_find(cf_section_sub_find(maincs, "modules"), "radutmp");
409         if(!cs) {
410                 fprintf(stderr, "%s: No configuration information in radutmp section of radiusd.conf!\n",
411                         argv[0]);
412                 exit(1);
413         }
414
415         cf_section_parse(cs, NULL, module_config);
416
417         /* Assign the correct path for the radutmp file */
418         radutmp_file = radutmpconfig.radutmp_fn;
419
420         /*
421          *      See if we are "fingerd".
422          */
423         if (strstr(argv[0], "fingerd")) {
424                 fingerd++;
425                 eol = "\r\n";
426                 if (showname < 0) showname = 0;
427         }
428         if (showname < 0) showname = 1;
429
430         if (fingerd) {
431                 /*
432                  *      Read first line of the input.
433                  */
434                 fgets(inbuf, 128, stdin);
435                 p = inbuf;
436                 while(*p == ' ' || *p == '\t') p++;
437                 if (*p == '/' && *(p + 1)) p += 2;
438                 while(*p == ' ' || *p == '\t') p++;
439                 for(q = p; *q && *q != '\r' && *q != '\n'; q++)
440                         ;
441                 *q = 0;
442
443                 /*
444                  *      See if we fingered a specific user.
445                  */
446                 ffile("header");
447                 if (*p) sys_finger(p);
448         }
449
450         /*
451          *      Show the users logged in on the terminal server(s).
452          */
453         if ((fp = fopen(radutmp_file, "r")) == NULL) {
454                 fprintf(stderr, "%s: Error reading %s: %s\n",
455                         progname, radutmp_file, strerror(errno));
456                 return 0;
457         }
458
459         /*
460          *      Don't print the headers if raw or RADIUS
461          */
462         if (!rawoutput && !radiusoutput) {
463                 fputs(showname ? hdr1 : hdr2, stdout);
464                 fputs(eol, stdout);
465         }
466
467         /*
468          *      Read the file, printing out active entries.
469          */
470         while (fread(&rt, sizeof(rt), 1, fp) == 1) {
471                 if (rt.type != P_LOGIN) continue; /* hide logout sessions */
472
473                 /*
474                  *      We don't show shell users if we are
475                  *      fingerd, as we have done that above.
476                  */
477                 if (hideshell && !strchr("PCS", rt.proto))
478                         continue;
479
480                 /*
481                  *      Print out sessions only for the given user.
482                  */
483                 if (user) {     /* only for a particular user */
484                         if (((user_cmp == 0) &&
485                              (strncasecmp(rt.login, user, strlen(user)) != 0)) ||
486                             ((user_cmp == 1) &&
487                              (strncmp(rt.login, user, strlen(user)) != 0))) {
488                                 continue;
489                         }
490                 }
491
492                 /*
493                  *      Print out only for the given NAS port.
494                  */
495                 if (~nas_port != 0) {
496                         if (rt.nas_port != nas_port) continue;
497                 }
498
499                 /*
500                  *      Print out only for the given NAS IP address
501                  */
502                 if (nas_ip_address != INADDR_NONE) {
503                         if (rt.nas_address != nas_ip_address) continue;
504                 }
505                 
506                 memcpy(session_id, rt.session_id, sizeof(rt.session_id));
507                 session_id[sizeof(rt.session_id)] = 0;
508                 
509                 if (!rawoutput && rt.nas_port > (showname ? 999 : 99999)) {
510                         portind = ">";
511                         portno = (showname ? 999 : 99999);
512                 } else {
513                         portind = "S";
514                         portno = rt.nas_port;
515                 }
516
517                 /*
518                  *      Print output as RADIUS attributes
519                  */
520                 if (radiusoutput) {
521                         memcpy(nasname, rt.login, sizeof(rt.login));
522                         nasname[sizeof(rt.login)] = '\0';
523
524                         librad_safeprint(nasname, -1, buffer,
525                                          sizeof(buffer));
526                         printf("User-Name = \"%s\"\n", buffer);
527
528                         librad_safeprint(session_id, -1, buffer,
529                                          sizeof(buffer));
530                         printf("Acct-Session-Id = \"%s\"\n", buffer);
531
532                         if (zap) printf("Acct-Status-Type = Stop\n");
533
534                         printf("NAS-IP-Address = %s\n",
535                                ip_hostname(buffer, sizeof(buffer),
536                                            rt.nas_address));
537                         printf("NAS-Port = %u\n", rt.nas_port);
538
539                         switch (rt.proto) {
540                                 case 'S':
541                                         printf("Service-Type = Framed-User\n");
542                                         printf("Framed-Protocol = SLIP\n");
543                                         break;
544                                 case 'P':
545                                         printf("Service-Type = Framed-User\n");
546                                         printf("Framed-Protocol = PPP\n");
547                                         break;
548                                 default:
549                                         printf("Service-type = Login-User\n");
550                                         break;
551                         }
552                         if (rt.framed_address != INADDR_NONE) {
553                                 printf("Framed-IP-Address = %s\n",
554                                        ip_hostname(buffer, sizeof(buffer),
555                                                    rt.framed_address));
556                         }
557                         
558                         /*
559                          *      Some sanity checks on the time
560                          */
561                         if ((rt.time <= now) &&
562                             (now - rt.time) <= (86400 * 365)) {
563                                 printf("Acct-Session-Time = %ld\n",
564                                        now - rt.time);
565                         }
566
567                         if (rt.caller_id[0] != '\0') {
568                                 memcpy(nasname, rt.caller_id,
569                                        sizeof(rt.caller_id));
570                                 nasname[sizeof(rt.caller_id)] = '\0';
571                                 
572                                 librad_safeprint(nasname, -1, buffer,
573                                                  sizeof(buffer));
574                                 printf("Calling-Station-Id = \"%s\"\n", buffer);
575                         }
576
577                         printf("\n"); /* separate entries with a blank line */
578                         continue;
579                 }
580
581                 /*
582                  *      Show the fill name, or not.
583                  */
584                 if (showname) {
585                         printf((rawoutput == 0? rfmt1: rfmt1r),
586                                rt.login,
587                                showcid ? rt.caller_id :
588                                (showsid? session_id : fullname(rt.login)),
589                                proto(rt.proto, rt.porttype),
590                                portind, portno,
591                                dotime(rt.time),
592                                ip_hostname(nasname, sizeof(nasname), rt.nas_address),
593                                hostname(othername, sizeof(othername), rt.framed_address), eol);
594                 } else {
595                         printf((rawoutput == 0? rfmt2: rfmt2r),
596                                rt.login,
597                                portind, portno,
598                                proto(rt.proto, rt.porttype),
599                                dotime(rt.time),
600                                ip_hostname(nasname, sizeof(nasname), rt.nas_address),
601                                hostname(othername, sizeof(othername), rt.framed_address),
602                                eol);
603                 }
604         }
605         fclose(fp);
606
607         return 0;
608 }
609