Applied Ere's global client-authentication patch.
[yazproxy-moved-to-github.git] / src / mod_helsinki.cpp
1 /* $Id: mod_helsinki.cpp,v 1.2 2006-06-28 23:38:23 adam Exp $
2    Copyright (c) 1998-2005, Index Data.
3
4 This file is part of the yaz-proxy.
5
6 YAZ proxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with YAZ proxy; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include <string.h>
23 #include <stdio.h>
24 #include <unistd.h>
25
26 #include <yazproxy/module.h>
27
28 #include <yaz/log.h>
29
30 #include <time.h>
31
32 #if HAVE_XSLT
33 #include <libxml/parser.h>
34 #include <libxml/tree.h>
35 #include <libxml/xinclude.h>
36 #include <libxslt/xsltutils.h>
37 #include <libxslt/transform.h>
38 #endif
39
40 struct IP_ADDRESS
41 {
42     unsigned int a1, a2, a3, a4;
43 };
44
45 void *my_init(void)
46 {
47     return 0;  // no private data for handler
48 }
49
50 void my_destroy(void *p)
51 {
52     // private data destroy
53 }
54
55 void zero_address(IP_ADDRESS *addr)
56 {
57     addr->a1 = addr->a2 = addr->a3 = addr->a4 = 0;
58 }
59
60 int str_to_address(const char *str, IP_ADDRESS *addr)
61 {
62     zero_address(addr);
63     return sscanf(str, "%3u.%3u.%3u.%3u", &addr->a1, &addr->a2, &addr->a3, &addr->a4);
64 }
65
66 void str_to_address_range(const char *str,
67                           IP_ADDRESS *range_lo,
68                           IP_ADDRESS *range_hi)
69 {
70     char lo[16], hi[16];
71     *lo = '\0';
72     *hi = '\0';
73     int num = sscanf(str, "%15[^-]-%15s", lo, hi);
74
75     if (num == 1)
76     {
77         // Create a range from a single address or a part of it (e.g. 192.168)
78         num = str_to_address(lo, range_lo);
79         if (num == 1)
80         {
81             range_hi->a1 = range_lo->a1;
82             range_hi->a2 = range_hi->a3 = range_hi->a4 = 255;
83         }
84         else if (num == 2)
85         {
86             range_hi->a1 = range_lo->a1;
87             range_hi->a2 = range_lo->a2;
88             range_hi->a3 = range_hi->a4 = 255;
89         }
90         else if (num == 3)
91         {
92             range_hi->a1 = range_lo->a1;
93             range_hi->a2 = range_lo->a2;
94             range_hi->a3 = range_lo->a3;
95             range_hi->a4 = 255;
96         }
97         else
98         {
99             range_hi->a1 = range_lo->a1;
100             range_hi->a2 = range_lo->a2;
101             range_hi->a3 = range_lo->a3;
102             range_hi->a4 = range_lo->a4;
103         }
104         return;
105     }
106
107     // If a range is specified, both ends need to be full addresses
108     if (str_to_address(lo, range_lo) != 4 || str_to_address(hi, range_hi) != 4)
109     {
110         zero_address(range_lo);
111         zero_address(range_hi);
112     }
113 }
114
115 unsigned int address_to_int(IP_ADDRESS addr)
116 {
117     return addr.a1 << 24 | addr.a2 << 16 | addr.a3 << 8 | addr.a4;
118 }
119
120 int my_authenticate(void *user_handle,
121                     const char *target_name,
122                     void *element_ptr,
123                     const char *user, const char *group, const char *password,
124                     const char *peer_IP)
125 {
126     // see if we have an "args" attribute
127     const char *args = 0;
128 #if HAVE_XSLT
129     xmlNodePtr ptr = (xmlNodePtr) element_ptr;
130     struct _xmlAttr *attr;
131
132     for (attr = ptr->properties; attr; attr = attr->next)
133     {
134         if (!strcmp((const char *) attr->name, "args") &&
135             attr->children && attr->children->type == XML_TEXT_NODE)
136             args = (const char *) attr->children->content;
137     }
138 #endif
139     // args holds args (or NULL if none are provided)
140
141     yaz_log(YLOG_LOG, "Authentication: authenticating user %s, address %s", user ? user : "(none)", peer_IP ? peer_IP : "-");
142
143     // authentication handler
144     char user_file[255], ip_file[255];
145     *user_file = '\0';
146     *ip_file = '\0';
147     sscanf(args, "%254[^:]:%254s", user_file, ip_file);
148
149     yaz_log(YLOG_DEBUG, "Authentication: user file: %s, ip file: %s", user_file, ip_file);
150
151     // Check if the IP address is listed in the file of allowed address ranges.
152     // The format of the file:
153     // 192.168.0
154     // 192.168.0.100
155     // 192.168.0.1-192.168.0.200
156     int status = YAZPROXY_RET_PERM;
157     if (ip_file && peer_IP)
158     {
159         yaz_log(YLOG_DEBUG, "Authentication: checking ip address");
160
161         const char *pIP = peer_IP;
162         if (strncmp(pIP, "tcp:", 4) == 0)
163             pIP += 4;
164         IP_ADDRESS peer_address;
165         if (str_to_address(pIP, &peer_address) != 4)
166             yaz_log(YLOG_WARN, "Authentication: could not decode peer IP address %s properly", pIP);
167         unsigned int peer_address_int = address_to_int(peer_address);
168
169         FILE *f = fopen(ip_file, "r");
170         if (!f)
171         {
172             yaz_log(YLOG_WARN, "Authentication: could not open ip authentication file %s", ip_file);
173                 return YAZPROXY_RET_PERM;
174         }
175         while (!feof(f))
176         {
177             char line[255];
178             *line = '\0';
179             fgets(line, 254, f);
180             line[254] = '\0';
181
182             // Remove comments
183             char *comment_pos = strchr(line, '#');
184             if (comment_pos)
185                 *comment_pos = '\0';
186
187             IP_ADDRESS range_lo, range_hi;
188             str_to_address_range(line, &range_lo, &range_hi);
189             if (address_to_int(range_lo) <= peer_address_int && peer_address_int <= address_to_int(range_hi))
190             {
191                 status = YAZPROXY_RET_OK;
192                 break;
193             }
194         }
195         fclose(f);
196         if (status == YAZPROXY_RET_OK)
197         {
198             yaz_log(YLOG_LOG, "Authentication: IP address %s allowed", pIP);
199             return YAZPROXY_RET_OK;
200         }
201     }
202
203     if (!user || !password || !*user_file)
204     {
205         yaz_log(YLOG_LOG, "Authentication: anonymous authentication failed");
206             return YAZPROXY_RET_PERM;
207     }
208
209     time_t current_time;
210     time(&current_time);
211     struct tm *local_time = localtime(&current_time);
212     char current_date[10];
213     sprintf(current_date, "%04d%02d%02d", local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday);
214
215     FILE *f = fopen(user_file, "r");
216     if (!f)
217     {
218         yaz_log(YLOG_WARN, "Authentication: could not open user authentication file %s", user_file);
219             return YAZPROXY_RET_PERM;
220     }
221     while (!feof(f))
222     {
223         char line[255];
224         *line = '\0';
225         fgets(line, 254, f);
226         line[254] = '\0';
227         char *p = strchr(line, '\n');
228         if (p) *p = '\0';
229
230         char f_user[255], f_password[255], f_expiry[255];
231         *f_user = '\0';
232         *f_password = '\0';
233         *f_expiry = '\0';
234         sscanf(line, "%254[^:]:%254[^:]:%254s", f_user, f_password, f_expiry);
235
236         if (strcmp(user, f_user) == 0 && strcmp(password, f_password) == 0 && (!*f_expiry || strcmp(current_date, f_expiry) <= 0))
237         {
238             status = YAZPROXY_RET_OK;
239             break;
240         }
241     }
242     fclose(f);
243     yaz_log(YLOG_LOG, "Authentication: %s for user %s", status == YAZPROXY_RET_OK ? "successful" : "failed", user);
244     return status;
245 }
246
247 Yaz_ProxyModule_int0 interface0 = {
248     my_init,
249     my_destroy,
250     my_authenticate
251 };
252
253 Yaz_ProxyModule_entry yazproxy_module = {
254     0,                            // interface version
255     "helsinki",                     // name
256     "Helsinki Module for YAZ Proxy",// description
257     &interface0
258 };
259
260 /*
261  * Local variables:
262  * c-basic-offset: 4
263  * indent-tabs-mode: nil
264  * End:
265  * vim: shiftwidth=4 tabstop=8 expandtab
266  */