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