perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[freeradius.git] / src / main / radzap.c
1 /*
2  * radzap.c     Zap a user from the radutmp and radwtmp file.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Alan DeKok <aland@ox.org>
22  */
23
24 #include "autoconf.h"
25 #include "libradius.h"
26
27 #include <sys/file.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <netdb.h>
33 #include <limits.h>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37
38 #ifdef HAVE_NETINET_IN_H
39 #  include <netinet/in.h>
40 #endif
41
42 #include "radiusd.h"
43 #include "radutmp.h"
44 #include "conffile.h"
45
46 const char *progname;
47 const char *radlog_dir = NULL;
48 const char *radius_dir = NULL;
49 const char *radacct_dir = NULL;
50 const char *radlib_dir = NULL;
51 radlog_dest_t radlog_dest = RADLOG_FILES;
52 int debug_flag = 0;
53 int auth_port = 0;
54 int acct_port;
55 int log_stripped_names;
56 struct main_config_t mainconfig;
57 uint32_t radiusip = INADDR_NONE;
58 static void usage(void);
59
60 struct radutmp_config_t {
61   char *radutmp_fn;
62 } radutmpconfig;
63
64 static CONF_PARSER module_config[] = {
65   { "filename", PW_TYPE_STRING_PTR, 0, &radutmpconfig.radutmp_fn,  RADUTMP },
66   { NULL, -1, 0, NULL, NULL }
67 };
68
69 #define LOCK_LEN sizeof(struct radutmp)
70
71 static int radutmp_lookup(struct radutmp *u, uint32_t nasaddr,
72                 uint32_t port, const char *user)
73 {
74         int fd;
75
76         if ((fd = open(radutmpconfig.radutmp_fn, O_RDONLY|O_CREAT, 0644)) >= 0) {
77                 /*
78                  *      Lock the utmp file.
79                  */
80                 rad_lockfd(fd, LOCK_LEN);
81
82                 /*
83                  *      Find the entry for this NAS / portno combination.
84                  */
85                 while (read(fd, u, sizeof(*u)) == sizeof(*u)) {
86                         if ((nasaddr != 0 && nasaddr != u->nas_address) ||
87                                         (port != u->nas_port) ||
88                                         (user != NULL &&
89                                         strncmp(u->login, user, sizeof u->login) != 0) ||
90                                         u->type != P_LOGIN)
91                                 continue;
92                         /*
93                          *      Match. Zap it.
94                          */
95                         close(fd);
96                         return 1;
97                 }
98                 close(fd);
99         }
100         return 0;
101 }
102 static int do_accton_packet(uint32_t nasaddr);
103 static int do_stop_packet(const struct radutmp *u);
104
105 /*
106  *  Display the syntax for starting this program.
107  */
108 static void usage(void)
109 {
110         fprintf(stderr,
111                         "Usage: %s [-d raddb] [-p acct_port] [-r servername|serverip] termserver [port] [user]\n", progname);
112         fprintf(stderr, "Options:\n\n");
113         fprintf(stderr, "  -d raddb        Set the raddb directory (default is %s)\n", RADIUS_DIR);
114         fprintf(stderr, "  -p acct_port    Accounting port on radius server\n");
115         fprintf(stderr, "  -r radserver    Radius server name or IP address\n");
116         fprintf(stderr, "  termserver      Terminal Server (NAS) name or IP address to match, can be '' for any\n");
117         fprintf(stderr, "  [port]          Terminal Server port to match\n");
118         fprintf(stderr, "  [user]          Login account to match\n");
119         exit(1);
120 }
121
122
123 /*
124  *      Zap a user from the radutmp and radwtmp file.
125  */
126 int main(int argc, char **argv)
127 {
128         CONF_SECTION *cs;
129         NAS *nas;
130         uint32_t ip = 0;
131         uint32_t nas_port = ~0;
132         char *user = NULL;
133         char *s;
134         char buf[256];
135         struct radutmp u;
136         int argval;
137
138         progname = argv[0];
139
140         radius_dir = strdup(RADIUS_DIR);
141
142         /*  Process the options.  */
143         while ((argval = getopt(argc, argv, "d:p:r:")) != EOF) {
144
145                 switch(argval) {
146
147                         case 'd':
148                                 if (radius_dir) free(radius_dir);
149                                 radius_dir = strdup(optarg);
150                                 break;
151                         case 'p':
152                                 acct_port = atoi(optarg);
153                                 break;
154
155                         case 'r':
156                                 if ((radiusip = ip_getaddr(optarg)) == INADDR_NONE) {
157                                         fprintf(stderr, "%s: %s: radius server unknown\n",
158                                                 progname, optarg);
159                                         exit(1);
160                                 }
161                                 break;
162
163                         default:
164                                 usage();
165                                 exit(1);
166                 }
167         }
168
169
170         if (argc == optind) {   /* no terminal server specified */
171                 usage();
172                 exit(1);
173         }
174
175         if (argc > optind + 1) {        /* NAS port given */
176                 s = argv[optind+1];
177                 if (*s == 's' || *s == 'S') s++;
178                 nas_port = strtoul(s, NULL, 10);
179         }
180
181         if (argc > optind + 2) {        /* username (login) given */
182                 user = argv[optind+2];
183         }
184
185         /*
186          *      Find the IP address of the terminal server.
187          */
188         if ((nas = nas_findbyname(argv[optind])) == NULL && argv[optind][0] != 0) {
189                 if ((ip = ip_getaddr(argv[optind])) == INADDR_NONE) {
190                         fprintf(stderr, "%s: host not found.\n", argv[optind]);
191                         exit(1);
192                 }
193         }
194         if (nas != NULL)
195                 ip = nas->ipaddr;
196
197         /*
198          *      Ensure that the configuration is initialized.
199          */
200         memset(&mainconfig, 0, sizeof(mainconfig));
201
202         /* Read radiusd.conf */
203         if (read_mainconfig(0) < 0) {
204                 fprintf(stderr, "%s: Error reading radiusd.conf.\n", argv[0]);
205                 exit(1);
206         }
207
208         /* Read the radutmp section of radiusd.conf */
209         cs = cf_section_sub_find(cf_section_find("modules"), "radutmp");
210         if(!cs) {
211                 fprintf(stderr, "%s: No configuration information in radutmp section of radiusd.conf!\n",
212                         argv[0]);
213                 exit(1);
214         }
215
216         cf_section_parse(cs, NULL, module_config);
217
218         printf("%s: zapping termserver %s, port %u",
219                 progname, ip_hostname(buf, sizeof(buf), ip), nas_port);
220         if (user != NULL)
221                 printf(", user %s", user);
222         printf("\n");
223
224         if (nas_port == ~0) {
225                 return do_accton_packet(ip);
226         }
227
228         if (!radutmp_lookup(&u, ip, nas_port, user)) {
229                 fprintf(stderr, "Entry not found\n");
230                 return 1;
231         }
232
233         return do_stop_packet(&u);
234 }
235
236 static int getport(const char *name)
237 {
238         struct servent *svp;
239
240         svp = getservbyname(name, "udp");
241         if (svp == NULL) {
242                 return 0;
243         }
244
245         return ntohs(svp->s_port);
246 }
247
248 static const char *getsecret(uint32_t server)
249 {
250         RADCLIENT *cl;
251
252         cl = client_find(server);
253         if (cl == NULL) {
254                 char buf[32];
255                 radlog(L_ERR|L_CONS, "No clients entry for %s",
256                        ip_ntoa(buf,server));
257                 exit(1);
258         }
259         return (const char *)cl->secret;
260 }
261
262 /* Packet-fabrication macros. Don't stare directly at them without protective
263  * eye gear */
264 #define PAIR(n,v,t,e) do { \
265   if(!(vp=paircreate(n, t))) { \
266     radlog(L_ERR|L_CONS, "no memory"); \
267     pairfree(&req->vps); \
268     return 1; \
269   } \
270   vp->e=v; \
271   pairadd(&req->vps, vp); \
272 } while(0)
273 #define INTPAIR(n,v) PAIR(n,v,PW_TYPE_INTEGER,lvalue)
274 #define IPPAIR(n,v) PAIR(n,v,PW_TYPE_IPADDR,lvalue)
275 #define STRINGPAIR(n,v) do { \
276   if(!(vp=paircreate(n, PW_TYPE_STRING))) { \
277     radlog(L_ERR|L_CONS, "no memory"); \
278     pairfree(&req->vps); \
279     return 1; \
280   } \
281   strNcpy((char *)vp->strvalue, v, sizeof vp->strvalue); \
282   vp->length=strlen(v); \
283   pairadd(&req->vps, vp); \
284 } while(0)
285
286 static int do_packet(int allports, uint32_t nasaddr, const struct radutmp *u)
287 {
288         int i, retries=5, timeout=3;
289         struct timeval tv;
290         RADIUS_PACKET *req, *rep = NULL;
291         VALUE_PAIR *vp;
292         const char *secret;
293
294         if ((req = rad_alloc(1)) == NULL) {
295                 librad_perror("radzap");
296                 exit(1);
297         }
298         req->id = getpid() & 0xFF;
299         req->code = PW_ACCOUNTING_REQUEST;
300         req->dst_port = acct_port;
301         if(req->dst_port == 0)
302                 req->dst_port = getport("radacct");
303         if(req->dst_port == 0)
304                 req->dst_port = PW_ACCT_UDP_PORT;
305         if (radiusip == INADDR_NONE) {
306                 req->dst_ipaddr = ip_getaddr("localhost");
307         }
308         else {
309                 req->dst_ipaddr = radiusip;
310         }
311         if(!req->dst_ipaddr)
312                 req->dst_ipaddr = 0x7f000001;
313         req->vps = NULL;
314         secret = getsecret(req->dst_ipaddr);
315
316         if(allports != 0) {
317                 INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_ACCOUNTING_OFF);
318                 IPPAIR(PW_NAS_IP_ADDRESS, nasaddr);
319                 INTPAIR(PW_ACCT_DELAY_TIME, 0);
320         } else {
321                 char login[sizeof u->login+1];
322                 char session_id[sizeof u->session_id+1];
323                 strNcpy(login, u->login, sizeof login);
324                 strNcpy(session_id, u->session_id, sizeof session_id);
325                 INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
326                 IPPAIR(PW_NAS_IP_ADDRESS, u->nas_address);
327                 INTPAIR(PW_ACCT_DELAY_TIME, 0);
328                 STRINGPAIR(PW_USER_NAME, login);
329                 INTPAIR(PW_NAS_PORT, u->nas_port);
330                 STRINGPAIR(PW_ACCT_SESSION_ID, session_id);
331                 if(u->proto=='P') {
332                         INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
333                         INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP);
334                 } else if(u->proto=='S') {
335                         INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
336                         INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP);
337                 } else {
338                         INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */
339                 }
340                 IPPAIR(PW_FRAMED_IP_ADDRESS, u->framed_address);
341                 INTPAIR(PW_ACCT_SESSION_TIME, 0);
342                 INTPAIR(PW_ACCT_INPUT_OCTETS, 0);
343                 INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0);
344                 INTPAIR(PW_ACCT_INPUT_PACKETS, 0);
345                 INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0);
346         }
347         if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
348                 perror("radzap: socket: ");
349                 exit(1);
350         }
351
352         for (i = 0; i < retries; i++) {
353                 fd_set rdfdesc;
354
355                 rad_send(req, NULL, secret);
356
357                 /* And wait for reply, timing out as necessary */
358                 FD_ZERO(&rdfdesc);
359                 FD_SET(req->sockfd, &rdfdesc);
360
361                 tv.tv_sec = (int)timeout;
362                 tv.tv_usec = 1000000 * (timeout - (int)timeout);
363
364                 /* Something's wrong if we don't get exactly one fd. */
365                 if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) {
366                         continue;
367                 }
368
369                 rep = rad_recv(req->sockfd);
370                 if (rep != NULL) {
371                         break;
372                 } else {        /* NULL: couldn't receive the packet */
373                         librad_perror("radzap:");
374                         exit(1);
375                 }
376         }
377
378         /* No response or no data read (?) */
379         if (i == retries) {
380                 fprintf(stderr, "%s: no response from server\n", progname);
381                 exit(1);
382         }
383
384         if (rad_decode(rep, req, secret) != 0) {
385                 librad_perror("rad_decode");
386                 exit(1);
387         }
388
389         vp_printlist(stdout, rep->vps);
390         return 0;
391 }
392
393 static int do_accton_packet(uint32_t nasaddr)
394 {
395         return do_packet(1, nasaddr, 0);
396 }
397
398 static int do_stop_packet(const struct radutmp *u)
399 {
400         return do_packet(0, 0, u);
401 }