Bump version to 1.3.2
[yazproxy-moved-to-github.git] / src / mod_helsinki.cpp
1 /* $Id: mod_helsinki.cpp,v 1.4 2007-10-08 08:14:02 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 YAZ_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 YAZ_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         if (strncmp(pIP, "::ffff:", 7) == 0)
165             pIP += 7;
166         IP_ADDRESS peer_address;
167         if (str_to_address(pIP, &peer_address) != 4)
168             yaz_log(YLOG_WARN, "Authentication: could not decode peer IP address %s properly", pIP);
169         unsigned int peer_address_int = address_to_int(peer_address);
170
171         FILE *f = fopen(ip_file, "r");
172         if (!f)
173         {
174             yaz_log(YLOG_WARN, "Authentication: could not open ip authentication file %s", ip_file);
175                 return YAZPROXY_RET_PERM;
176         }
177         while (!feof(f))
178         {
179             char line[255];
180             *line = '\0';
181             fgets(line, 254, f);
182             line[254] = '\0';
183
184             // Remove comments
185             char *comment_pos = strchr(line, '#');
186             if (comment_pos)
187                 *comment_pos = '\0';
188
189             IP_ADDRESS range_lo, range_hi;
190             str_to_address_range(line, &range_lo, &range_hi);
191             if (address_to_int(range_lo) <= peer_address_int && peer_address_int <= address_to_int(range_hi))
192             {
193                 status = YAZPROXY_RET_OK;
194                 break;
195             }
196         }
197         fclose(f);
198         if (status == YAZPROXY_RET_OK)
199         {
200             yaz_log(YLOG_LOG, "Authentication: IP address %s allowed", pIP);
201             return YAZPROXY_RET_OK;
202         }
203     }
204
205     if (!user || !password || !*user_file)
206     {
207         yaz_log(YLOG_LOG, "Authentication: anonymous authentication failed");
208             return YAZPROXY_RET_PERM;
209     }
210
211     time_t current_time;
212     time(&current_time);
213     struct tm *local_time = localtime(&current_time);
214     char current_date[10];
215     sprintf(current_date, "%04d%02d%02d", local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday);
216
217     FILE *f = fopen(user_file, "r");
218     if (!f)
219     {
220         yaz_log(YLOG_WARN, "Authentication: could not open user authentication file %s", user_file);
221             return YAZPROXY_RET_PERM;
222     }
223     while (!feof(f))
224     {
225         char line[255];
226         *line = '\0';
227         fgets(line, 254, f);
228         line[254] = '\0';
229         char *p = strchr(line, '\n');
230         if (p) *p = '\0';
231
232         char f_user[255], f_password[255], f_expiry[255];
233         *f_user = '\0';
234         *f_password = '\0';
235         *f_expiry = '\0';
236         sscanf(line, "%254[^:]:%254[^:]:%254s", f_user, f_password, f_expiry);
237
238         if (strcmp(user, f_user) == 0 && strcmp(password, f_password) == 0 && (!*f_expiry || strcmp(current_date, f_expiry) <= 0))
239         {
240             status = YAZPROXY_RET_OK;
241             break;
242         }
243     }
244     fclose(f);
245     yaz_log(YLOG_LOG, "Authentication: %s for user %s", status == YAZPROXY_RET_OK ? "successful" : "failed", user);
246     return status;
247 }
248
249 Yaz_ProxyModule_int0 interface0 = {
250     my_init,
251     my_destroy,
252     my_authenticate
253 };
254
255 Yaz_ProxyModule_entry yazproxy_module = {
256     0,                            // interface version
257     "helsinki",                     // name
258     "Helsinki Module for YAZ Proxy",// description
259     &interface0
260 };
261
262 /*
263  * Local variables:
264  * c-basic-offset: 4
265  * indent-tabs-mode: nil
266  * End:
267  * vim: shiftwidth=4 tabstop=8 expandtab
268  */