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