import from HEAD
[freeradius.git] / src / main / session.c
1 /*
2  * session.c    session management
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  */
22
23
24 #include        "autoconf.h"
25
26 #include        <stdio.h>
27 #include        <stdlib.h>
28 #include        <string.h>
29
30 #ifdef HAVE_UNISTD_H
31 #include        <unistd.h>
32 #endif
33
34 #include        <signal.h>
35 #include        <errno.h>
36 #include        <sys/wait.h>
37
38 #ifdef HAVE_NETINET_IN_H
39 #include        <netinet/in.h>
40 #endif
41
42 #include        "radiusd.h"
43 #include        "rad_assert.h"
44 #include        "modules.h"
45
46 /* End a session by faking a Stop packet to all accounting modules */
47 int session_zap(REQUEST *request, uint32_t nasaddr, unsigned int port,
48                 const char *user,
49                 const char *sessionid, uint32_t cliaddr, char proto)
50 {
51         REQUEST *stopreq;
52         VALUE_PAIR *vp, *userpair;
53         int ret;
54
55         stopreq = request_alloc_fake(request);
56         stopreq->packet->code = PW_ACCOUNTING_REQUEST; /* just to be safe */
57         rad_assert(stopreq != NULL);
58
59         /* Hold your breath */
60 #define PAIR(n,v,t,e) do { \
61                 if(!(vp = paircreate(n, t))) { \
62                         radlog(L_ERR|L_CONS, "no memory"); \
63                         pairfree(&(stopreq->packet->vps)); \
64                         return 0; \
65                 } \
66                 vp->e = v; \
67                 pairadd(&(stopreq->packet->vps), vp); \
68         } while(0)
69 #define INTPAIR(n,v) PAIR(n,v,PW_TYPE_INTEGER,lvalue)
70 #define IPPAIR(n,v) PAIR(n,v,PW_TYPE_IPADDR,lvalue)
71 #define STRINGPAIR(n,v) do { \
72         if(!(vp = paircreate(n, PW_TYPE_STRING))) { \
73                 radlog(L_ERR|L_CONS, "no memory"); \
74                 pairfree(&(stopreq->packet->vps)); \
75                 return 0; \
76         } \
77         strNcpy((char *)vp->strvalue, v, sizeof vp->strvalue); \
78         vp->length = strlen(v); \
79         pairadd(&(stopreq->packet->vps), vp); \
80         } while(0)
81
82         INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
83         IPPAIR(PW_NAS_IP_ADDRESS, nasaddr);
84         INTPAIR(PW_ACCT_DELAY_TIME, 0);
85         STRINGPAIR(PW_USER_NAME, user);
86         userpair = vp;
87         INTPAIR(PW_NAS_PORT, port);
88         STRINGPAIR(PW_ACCT_SESSION_ID, sessionid);
89         if(proto == 'P') {
90                 INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
91                 INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP);
92         } else if(proto == 'S') {
93                 INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
94                 INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP);
95         } else {
96                 INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */
97         }
98         if(cliaddr != 0)
99                 IPPAIR(PW_FRAMED_IP_ADDRESS, cliaddr);
100         INTPAIR(PW_ACCT_SESSION_TIME, 0);
101         INTPAIR(PW_ACCT_INPUT_OCTETS, 0);
102         INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0);
103         INTPAIR(PW_ACCT_INPUT_PACKETS, 0);
104         INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0);
105
106         stopreq->username = userpair;
107         stopreq->password = NULL;
108
109         ret = rad_accounting(stopreq);
110
111         /*
112          *  We've got to clean it up by hand, because no one else will.
113          */
114         request_free(&stopreq);
115
116         return ret;
117 }
118
119
120 /*
121  *      Check one terminal server to see if a user is logged in.
122  */
123 int rad_check_ts(uint32_t nasaddr, unsigned int portnum, const char *user,
124                  const char *session_id)
125 {
126         pid_t   pid, child_pid;
127         int     status;
128         int     n;
129         char    address[16];
130         char    port[11];
131         RADCLIENT *cl;
132
133         /*
134          *      Find NAS type.
135          */
136         cl = client_find(nasaddr);
137         if (!cl) {
138                 /*
139                  *  Unknown NAS, so trusting radutmp.
140                  */
141                 DEBUG2("checkrad: Unknown NAS %s, not checking",
142                        ip_ntoa(address, nasaddr));
143                 return 1;
144         }
145
146         /*
147          *  No nastype, or nas type 'other', trust radutmp.
148          */
149         if ((cl->nastype[0] == '\0') ||
150             (strcmp(cl->nastype, "other") == 0)) {
151                 DEBUG2("checkrad: No NAS type, or type \"other\" not checking");
152                 return 1;
153         }
154
155         /*
156          *      Fork.
157          */
158         if ((pid = rad_fork(1)) < 0) { /* do wait for the fork'd result */
159                 radlog(L_ERR, "Accounting: Failed in fork(): Cannot run checkrad\n");
160                 return -1;
161         }
162
163         if (pid > 0) {
164                 int found = 0;
165
166                 /*
167                  *      Parent - Wait for checkrad to terminate.
168                  *      We timeout in 10 seconds.
169                  */
170                 child_pid = -1;
171                 for (n = 0; n < 10; n++) {
172                         sleep(1);
173                         child_pid = rad_waitpid(pid, &status, WNOHANG);
174                         if ((child_pid < 0) || (child_pid == pid)) {
175                                 found = 1;
176                                 break;
177                         }
178                 }
179
180                 /*
181                  *      It's taking too long.  Stop waiting for it.
182                  *
183                  *      Don't bother to kill it, as we don't care what
184                  *      happens to it now.
185                  */
186                 if (!found) {
187                         radlog(L_ERR, "Check-TS: timeout waiting for checkrad");
188                         return 2;
189                 }
190
191                 if (child_pid < 0) {
192                         radlog(L_ERR, "Check-TS: unknown error in waitpid()");
193                         return 2;
194                 }
195
196                 return WEXITSTATUS(status);
197         }
198
199         closefrom(3);
200
201         /*
202          *  We don't close fd's 0, 1, and 2.  If we're in debugging mode,
203          *  then they should go to stdout (etc), along with the other
204          *  server log messages.
205          *
206          *  If we're not in debugging mode, then the code in radiusd.c
207          *  takes care of connecting fd's 0, 1, and 2 to /dev/null.
208          */
209
210         ip_ntoa(address, nasaddr);
211         snprintf(port, 11, "%u", portnum);
212
213 #ifdef __EMX__
214         /* OS/2 can't directly execute scripts then we call the command
215            processor to execute checkrad
216         */
217         execl(getenv("COMSPEC"), "", "/C","checkrad", cl->nastype, address, port,
218                 user, session_id, NULL);
219 #else
220         execl(mainconfig.checkrad, "checkrad", cl->nastype, address, port,
221                 user, session_id, NULL);
222 #endif
223         radlog(L_ERR, "Check-TS: exec %s: %s", mainconfig.checkrad, strerror(errno));
224
225         /*
226          *      Exit - 2 means "some error occured".
227          */
228         exit(2);
229         return -1;
230 }