1 /*
2 * Copyright (C) 2014 Red Hat
3 * Copyright (C) 2017 Nikos Mavrogiannopoulos
4 *
5 * This file is part of ocserv.
6 *
7 * ocserv is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * ocserv is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/select.h>
29 #include <sys/wait.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <sys/socket.h>
33 #include <netdb.h>
34 #include <system.h>
35 #include <errno.h>
36 #include <sys/ioctl.h>
37 #include <sys/un.h>
38 #include <common.h>
39 #include <syslog.h>
40 #include <vpn.h>
41 #include <base64-helper.h>
42 #include <tlslib.h>
43 #include <sec-mod.h>
44 #include <ccan/hash/hash.h>
45 #include <ccan/htable/htable.h>
46
47 #include <gnutls/gnutls.h>
48 #include <gnutls/crypto.h>
49
rehash(const void * _e,void * unused)50 static size_t rehash(const void *_e, void *unused)
51 {
52 const client_entry_st *e = _e;
53
54 return hash_any(e->sid, sizeof(e->sid), 0);
55 }
56
sec_mod_client_db_init(sec_mod_st * sec)57 void *sec_mod_client_db_init(sec_mod_st *sec)
58 {
59 struct htable *db = talloc(sec, struct htable);
60 if (db == NULL)
61 return NULL;
62
63 htable_init(db, rehash, NULL);
64 sec->client_db = db;
65
66 return db;
67 }
68
sec_mod_client_db_deinit(sec_mod_st * sec)69 void sec_mod_client_db_deinit(sec_mod_st *sec)
70 {
71 struct htable *db = sec->client_db;
72
73 htable_clear(db);
74 talloc_free(db);
75 }
76
77 /* The number of elements */
sec_mod_client_db_elems(sec_mod_st * sec)78 unsigned sec_mod_client_db_elems(sec_mod_st *sec)
79 {
80 struct htable *db = sec->client_db;
81
82 if (db)
83 return db->elems;
84 else
85 return 0;
86 }
87
new_client_entry(sec_mod_st * sec,struct vhost_cfg_st * vhost,const char * ip,unsigned pid)88 client_entry_st *new_client_entry(sec_mod_st *sec, struct vhost_cfg_st *vhost, const char *ip, unsigned pid)
89 {
90 struct htable *db = sec->client_db;
91 client_entry_st *e, *te;
92 int ret;
93 int retries = 3;
94 time_t now;
95
96 e = talloc_zero(db, client_entry_st);
97 if (e == NULL) {
98 return NULL;
99 }
100
101 if (ip)
102 strlcpy(e->acct_info.remote_ip, ip, sizeof(e->acct_info.remote_ip));
103 e->acct_info.id = pid;
104 e->vhost = vhost;
105
106 do {
107 ret = gnutls_rnd(GNUTLS_RND_RANDOM, e->sid, sizeof(e->sid));
108 if (ret < 0) {
109 seclog(sec, LOG_ERR, "error generating SID");
110 goto fail;
111 }
112
113 e->sid[0] = sec->sec_mod_instance_id;
114 seclog(sec, LOG_INFO, "sec-mod instance %d issue cookie", sec->sec_mod_instance_id);
115
116 /* check if in use */
117 te = find_client_entry(sec, e->sid);
118 } while(te != NULL && retries-- >= 0);
119
120 if (te != NULL) {
121 seclog(sec, LOG_ERR,
122 "could not generate a unique SID!");
123 goto fail;
124 }
125
126 calc_safe_id(e->sid, SID_SIZE, (char *)e->acct_info.safe_id, sizeof(e->acct_info.safe_id));
127 now = time(0);
128 e->exptime = now + vhost->perm_config.config->cookie_timeout + AUTH_SLACK_TIME;
129 e->created = now;
130
131 if (htable_add(db, rehash(e, NULL), e) == 0) {
132 seclog(sec, LOG_ERR,
133 "could not add client entry to hash table");
134 goto fail;
135 }
136
137 return e;
138
139 fail:
140 talloc_free(e);
141 return NULL;
142 }
143
client_entry_cmp(const void * _c1,void * _c2)144 static bool client_entry_cmp(const void *_c1, void *_c2)
145 {
146 const struct client_entry_st *c1 = _c1;
147 struct client_entry_st *c2 = _c2;
148
149 if (memcmp(c1->sid, c2->sid, SID_SIZE) == 0)
150 return 1;
151 return 0;
152 }
153
find_client_entry(sec_mod_st * sec,uint8_t sid[SID_SIZE])154 client_entry_st *find_client_entry(sec_mod_st *sec, uint8_t sid[SID_SIZE])
155 {
156 struct htable *db = sec->client_db;
157 client_entry_st t;
158
159 memcpy(t.sid, sid, SID_SIZE);
160
161 return htable_get(db, rehash(&t, NULL), client_entry_cmp, &t);
162 }
163
clean_entry(sec_mod_st * sec,client_entry_st * e)164 static void clean_entry(sec_mod_st *sec, client_entry_st * e)
165 {
166 sec_auth_user_deinit(sec, e);
167 talloc_free(e->msg_str);
168 talloc_free(e);
169 }
170
cleanup_client_entries(sec_mod_st * sec)171 void cleanup_client_entries(sec_mod_st *sec)
172 {
173 struct htable *db = sec->client_db;
174 client_entry_st *t;
175 struct htable_iter iter;
176 time_t now = time(0);
177
178 t = htable_first(db, &iter);
179 while (t != NULL) {
180 if IS_CLIENT_ENTRY_EXPIRED_FULL(sec, t, now, 1) {
181 htable_delval(db, &iter);
182 clean_entry(sec, t);
183 }
184 t = htable_next(db, &iter);
185
186 }
187 }
188
del_client_entry(sec_mod_st * sec,client_entry_st * e)189 void del_client_entry(sec_mod_st *sec, client_entry_st * e)
190 {
191 struct htable *db = sec->client_db;
192
193 htable_del(db, rehash(e, NULL), e);
194 clean_entry(sec, e);
195 }
196
expire_client_entry(sec_mod_st * sec,client_entry_st * e)197 void expire_client_entry(sec_mod_st *sec, client_entry_st * e)
198 {
199 time_t now;
200
201 if (e->in_use > 0)
202 e->in_use--;
203 if (e->in_use == 0) {
204 if (e->vhost->perm_config.config->persistent_cookies == 0 && (e->discon_reason == REASON_SERVER_DISCONNECT ||
205 e->discon_reason == REASON_SESSION_TIMEOUT || (e->session_is_open && e->discon_reason == REASON_USER_DISCONNECT))) {
206 seclog(sec, LOG_INFO, "invalidating session of user '%s' "SESSION_STR,
207 e->acct_info.username, e->acct_info.safe_id);
208 /* immediately disconnect the user */
209 del_client_entry(sec, e);
210 } else {
211 now = time(0);
212 /* We intentionally don't close the session immediatelly on
213 * REASON_USER_DISCONNECT, as some anyconect clients
214 * explicitly disconnect with the intention to reconnect
215 * seconds later. */
216 if (e->discon_reason == REASON_USER_DISCONNECT) {
217 if (!e->vhost->perm_config.config->persistent_cookies || (now+AUTH_SLACK_TIME >= e->exptime))
218 e->exptime = now + AUTH_SLACK_TIME;
219 } else {
220 e->exptime = now + e->vhost->perm_config.config->cookie_timeout + AUTH_SLACK_TIME;
221 }
222 seclog(sec, LOG_INFO, "temporarily closing session for %s "SESSION_STR, e->acct_info.username, e->acct_info.safe_id);
223 }
224 }
225 }
226