Branch data Line data Source code
1 : : /* zxidses.c - Handwritten functions for SP session handling
2 : : * Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 : : * Copyright (c) 2006-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4 : : * Author: Sampo Kellomaki (sampo@iki.fi)
5 : : * This is confidential unpublished proprietary source code of the author.
6 : : * NO WARRANTY, not even implied warranties. Contains trade secrets.
7 : : * Distribution prohibited unless authorized in writing.
8 : : * Licensed under Apache License 2.0, see file COPYING.
9 : : * $Id: zxidses.c,v 1.30 2010-01-08 02:10:09 sampo Exp $
10 : : *
11 : : * 12.8.2006, created --Sampo
12 : : * 16.1.2007, split from zxidlib.c --Sampo
13 : : * 5.2.2007, added EPR handling --Sampo
14 : : * 7.8.2008, added session lookup by NameID --Sampo
15 : : * 7.10.2008, added documentation --Sampo
16 : : * 12.2.2010, added pthread locking --Sampo
17 : : *
18 : : * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
19 : : */
20 : :
21 : : #include "platform.h" /* for dirent.h */
22 : :
23 : : #include <sys/types.h>
24 : : #include <sys/stat.h>
25 : : #include <fcntl.h>
26 : : #include <string.h>
27 : : #include <stdio.h>
28 : : #include <errno.h>
29 : :
30 : : #include "errmac.h"
31 : : #include "zxid.h"
32 : : #include "zxidutil.h"
33 : : #include "zxidconf.h"
34 : : #include "saml2.h"
35 : : #include "c/zx-ns.h"
36 : : #include "c/zx-e-data.h"
37 : :
38 : : /* ============== Sessions ============== */
39 : :
40 : : #define ZXID_MAX_SES (256) /* Just the session nid and path to assertion */
41 : :
42 : : /*() When session is loaded, we only get the reference to assertion. This
43 : : * is to avoid parsing overhead when the assertion really is not needed.
44 : : * But when the assertion is needed, you have to call this function to load
45 : : * it from file (under /var/zxid/log/rely/EID/a7n/AID) and parse it. */
46 : :
47 : : /* Called by: zxid_get_ses_idp, zxid_idp_loc, zxid_ses_to_pool, zxid_simple_ses_active_cf, zxid_snarf_eprs_from_ses, zxid_sp_loc, zxid_sp_mni_redir, zxid_sp_mni_soap, zxid_sp_slo_redir, zxid_sp_slo_soap */
48 : : int zxid_get_ses_sso_a7n(zxid_conf* cf, zxid_ses* ses)
49 : 156 : {
50 : : struct zx_sa_EncryptedID_s* encid;
51 : : struct zx_str* ss;
52 : : struct zx_root_s* r;
53 : : struct zx_str* subj;
54 : : int gotall;
55 [ + + + - : 156 : if (ses->a7n || ses->a7n11 || ses->a7n12) /* already in cache */
- + ]
56 : 102 : return 1;
57 [ + + ]: 54 : if (!ses->sso_a7n_path) {
58 [ + + + - : 37 : D("Session object does not have any SSO assertion sid(%s)", STRNULLCHK(ses->sid));
- + ]
59 : 37 : return 0;
60 : : }
61 : 17 : ses->sso_a7n_buf = read_all_alloc(cf->ctx, "get_ses_sso_a7n", 1, &gotall, "%s", ses->sso_a7n_path);
62 [ - + ]: 17 : if (!ses->sso_a7n_buf)
63 : 0 : return 0;
64 : :
65 : : DD("a7n(%s)", ses->sso_a7n_buf);
66 : 17 : r = zx_dec_zx_root(cf->ctx, gotall, ses->sso_a7n_buf, "sso a7n");
67 [ - + ]: 17 : if (!r) {
68 [ # # ]: 0 : ERR("Failed to decode the sso assertion of session sid(%s) from path(%s), a7n data(%.*s)",
69 : : STRNULLCHK(ses->sid), ses->sso_a7n_path, gotall, ses->sso_a7n_buf);
70 : 0 : return 0;
71 : : }
72 : :
73 : 17 : ses->a7n = r->Assertion;
74 : 17 : ses->a7n11 = r->sa11_Assertion;
75 : 17 : ses->a7n12 = r->ff12_Assertion;
76 [ + - + - ]: 34 : if (ses->a7n && ses->a7n->Subject) {
77 : 17 : ses->nameid = ses->a7n->Subject->NameID;
78 : 17 : encid = ses->a7n->Subject->EncryptedID;
79 [ + - + - ]: 17 : if (!ses->nameid && encid) {
80 : 17 : ss = zxenc_privkey_dec(cf, encid->EncryptedData, encid->EncryptedKey);
81 : 17 : r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "ses nid");
82 [ - + ]: 17 : if (!r) {
83 : 0 : ERR("Failed to parse EncryptedID buf(%.*s)", ss->len, ss->s);
84 : 0 : return 0;
85 : : }
86 : 17 : ses->nameid = r->NameID;
87 : : }
88 [ + - ]: 17 : if (ses->nameid)
89 [ + - + - : 17 : subj = ZX_GET_CONTENT(ses->nameid);
+ - ]
90 [ # # ]: 0 : } else if (ses->a7n11)
91 [ # # # # : 0 : subj = ZX_GET_CONTENT(ses->a7n11->AuthenticationStatement->Subject->NameIdentifier);
# # ]
92 [ # # ]: 0 : else if (ses->a7n12)
93 [ # # # # : 0 : subj = ZX_GET_CONTENT(ses->a7n12->AuthenticationStatement->Subject->NameIdentifier);
# # ]
94 : :
95 [ + - ]: 17 : if (subj) {
96 [ + - ]: 17 : if (ses->nid) {
97 [ - + ]: 17 : if (memcmp(ses->nid, subj->s, subj->len)) {
98 [ # # ]: 0 : ERR("Session sid(%s), nid(%s), SSO assertion in path(%s) had different nid(%.*s). a7n data(%.*s)",
99 : : STRNULLCHK(ses->sid), ses->nid, ses->sso_a7n_path, subj->len, subj->s, gotall, ses->sso_a7n_buf);
100 : : }
101 : : } else
102 : 0 : ses->nid = zx_str_to_c(cf->ctx, subj);
103 : 17 : ses->tgt = ses->nid;
104 : : } else
105 [ # # ]: 0 : ERR("Session sid(%s) SSO assertion in path(%s) did not have Name ID. a7n data(%.*s)",
106 : : STRNULLCHK(ses->sid), ses->sso_a7n_path, gotall, ses->sso_a7n_buf);
107 : 17 : return 1;
108 : : }
109 : :
110 : : /*() Get the IdP entity associated with the session. Generally this is figured out from
111 : : * the Issuer field of the SSO assertion that started the session. */
112 : :
113 : : /* Called by: zxid_sp_mni_redir, zxid_sp_mni_soap, zxid_sp_slo_redir, zxid_sp_slo_soap */
114 : : zxid_entity* zxid_get_ses_idp(zxid_conf* cf, zxid_ses* ses)
115 : 2 : {
116 [ - + ]: 2 : if (!zxid_get_ses_sso_a7n(cf, ses))
117 : 0 : return 0;
118 [ + - - + ]: 2 : if (!ses->a7n || ! ses->a7n->Issuer) {
119 : 0 : ERR("Session assertion is missing Issuer (the IdP) %p", ses->a7n);
120 : 0 : return 0;
121 : : }
122 [ + - + - : 2 : return zxid_get_ent_ss(cf, ZX_GET_CONTENT(ses->a7n->Issuer));
+ - ]
123 : : }
124 : :
125 : : /*() Allocate memory for session object. Used with zxid_simple_cf_ses(). */
126 : :
127 : : /* Called by: main, zxid_as_call, zxid_fetch_ses */
128 : : zxid_ses* zxid_alloc_ses(zxid_conf* cf)
129 : 42 : {
130 : 42 : zxid_ses* ses = ZX_ZALLOC(cf->ctx, zxid_ses);
131 : 42 : LOCK_INIT(ses->mx);
132 : 42 : return ses;
133 : : }
134 : :
135 : : /*(i) Allocate memory and get session object from the filesystem, populating
136 : : * attributes to pool so they are available for use. You mus obtain session id
137 : : * from some source. */
138 : :
139 : : /* Called by: zxcall_main */
140 : : zxid_ses* zxid_fetch_ses(zxid_conf* cf, const char* sid)
141 : 15 : {
142 : 15 : zxid_ses* ses = zxid_alloc_ses(cf);
143 [ + - + - ]: 15 : if (sid && sid[0])
144 [ + + ]: 15 : if (!zxid_get_ses(cf, ses, sid)) {
145 : 2 : ZX_FREE(cf->ctx, ses);
146 : 2 : return 0;
147 : : }
148 : 13 : zxid_ses_to_pool(cf, ses);
149 : 13 : return ses;
150 : : }
151 : :
152 : : /*() Get simple session object from the filesystem. This just gets the nameid
153 : : * and reference to the assertion. Use zxid_get_ses_sso_a7n() to actually
154 : : * load the assertion, if needed. Or zxid_ses_to_pool() if you need attributes
155 : : * as well. Returns 1 if session gotten, 0 if fail. */
156 : :
157 : : /* Called by: chkuid x2, main x5, zxid_az_base_cf, zxid_az_cf, zxid_fetch_ses, zxid_find_ses, zxid_simple_cf_ses */
158 : : int zxid_get_ses(zxid_conf* cf, zxid_ses* ses, const char* sid)
159 : 47 : {
160 : : char* p;
161 : : int gotall;
162 : : #if 0
163 : : if (cf->ses_cookie_name && ses->setcookie
164 : : && !memcmp(cf->ses_cookie_name, ses->setcookie, strlen(cf->ses_cookie_name)))
165 : : p = ses->setcookie;
166 : : else
167 : : p = 0;
168 : : ZERO(ses, sizeof(zxid_ses));
169 : : ses->magic = ZXID_SES_MAGIC;
170 : : ses->setcookie = p;
171 : : #else
172 : 47 : ZERO(ses, sizeof(zxid_ses));
173 : 47 : ses->magic = ZXID_SES_MAGIC;
174 : : #endif
175 : :
176 : 47 : gotall = strlen(sid);
177 [ - + ]: 47 : if (gotall != strspn(sid, safe_basis_64)) {
178 : 0 : ERR("EVIL Session ID(%s)", sid);
179 : 0 : return 0;
180 : : }
181 : :
182 : 47 : ses->sesbuf = ZX_ALLOC(cf->ctx, ZXID_MAX_SES);
183 : 47 : gotall = read_all(ZXID_MAX_SES-1, ses->sesbuf, "get_ses", 1,
184 : : "%s" ZXID_SES_DIR "%s/.ses", cf->path, sid);
185 [ + + ]: 47 : if (!gotall)
186 : 9 : return 0;
187 [ + - - + ]: 38 : D("ses(%.*s) len=%d sid(%s) sesptr=%p", gotall, ses->sesbuf, gotall, sid, ses);
188 : 38 : ses->sesbuf[gotall] = 0;
189 : : DD("ses(%s)", ses->sesbuf);
190 : 38 : ses->sid = zx_dup_cstr(cf->ctx, sid);
191 : :
192 : 38 : ses->nid = ses->sesbuf;
193 : 38 : p = strchr(ses->sesbuf, '|');
194 [ + - ]: 38 : if (!p) goto out;
195 : 38 : *p++ = 0;
196 : :
197 : 38 : ses->sso_a7n_path = p;
198 : 38 : p = strchr(p, '|');
199 [ + - ]: 38 : if (!p) goto out;
200 : 38 : *p++ = 0;
201 : :
202 : 38 : ses->sesix = p;
203 : 38 : p = strchr(p, '|');
204 [ + - ]: 38 : if (!p) goto out;
205 : 38 : *p++ = 0;
206 : :
207 : 38 : ses->an_ctx = p;
208 : 38 : p = strchr(p, '|');
209 [ + - ]: 38 : if (!p) goto out;
210 : 38 : *p++ = 0;
211 : :
212 : 38 : ses->uid = p;
213 : 38 : p = strchr(p, '|');
214 [ + - ]: 38 : if (!p) goto out;
215 : 38 : *p++ = 0;
216 : :
217 : 38 : ses->an_instant = atol(p);
218 : :
219 : 38 : out:
220 [ + - + - : 38 : D("GOT sesdir(%s" ZXID_SES_DIR "%s) uid(%s) nid(%s) sso_a7n_path(%s) sesix(%s) an_ctx(%s)", cf->path, ses->sid, STRNULLCHK(ses->uid), STRNULLCHK(ses->nid), STRNULLCHK(ses->sso_a7n_path), STRNULLCHK(ses->sesix), STRNULLCHK(ses->an_ctx));
+ - + - +
- + - -
+ ]
221 : 38 : return 1;
222 : : }
223 : :
224 : : /*() Create new session object in file system. The assertion must have
225 : : * been created separately.
226 : : *
227 : : * cf:: Configuration object
228 : : * ses:: Pointer to previously allocated and populated session object
229 : : * return:: 1 upon success, 0 on failure. */
230 : :
231 : : /* Called by: zxid_as_call_ses, zxid_pw_authn, zxid_sp_anon_finalize, zxid_sp_sso_finalize, zxid_wsp_validate_env */
232 : : int zxid_put_ses(zxid_conf* cf, zxid_ses* ses)
233 : 75 : {
234 : : char dir[ZXID_MAX_BUF];
235 : : char* buf;
236 : : struct zx_str* ss;
237 : :
238 [ + + ]: 75 : if (ses->sid) {
239 [ - + ]: 18 : if (strlen(ses->sid) != strspn(ses->sid, safe_basis_64)) {
240 : 0 : ERR("EVIL Session ID(%s)", ses->sid);
241 : 0 : return 0;
242 : : }
243 : : } else { /* New session */
244 : 57 : ss = zxid_mk_id(cf, "S", ZXID_ID_BITS);
245 : 57 : ses->sid = ss->s;
246 : 57 : ZX_FREE(cf->ctx, ss);
247 : : }
248 : :
249 : 75 : name_from_path(dir, sizeof(dir), "%s" ZXID_SES_DIR "%s", cf->path, ses->sid);
250 [ - + # # ]: 75 : if (MKDIR(dir, 0777) && errno != EEXIST) {
251 : 0 : perror("mkdir for session");
252 : 0 : ERR("Creating session directory(%s) failed, euid=%d egid=%d", dir, geteuid(), getegid());
253 : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "S", "EFILE", dir, "mkdir fail, permissions?");
254 : 0 : return 0;
255 : : }
256 : :
257 : 75 : buf = ZX_ALLOC(cf->ctx, ZXID_MAX_SES);
258 [ + + + + : 75 : if (!write_all_path_fmt("put_ses", ZXID_MAX_SES, buf,
+ + + + +
+ - + ]
259 : : "%s" ZXID_SES_DIR "%s/.ses", cf->path, ses->sid,
260 : : "%s|%s|%s|%s|%s|%d|",
261 : : STRNULLCHK(ses->nid),
262 : : STRNULLCHK(ses->sso_a7n_path),
263 : : STRNULLCHK(ses->sesix),
264 : : STRNULLCHK(ses->an_ctx),
265 : : STRNULLCHK(ses->uid),
266 : : ses->an_instant)) {
267 : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "S", "EFILE", ses->sid, "writing ses fail, permissions?");
268 : 0 : ZX_FREE(cf->ctx, buf);
269 : 0 : return 0;
270 : : }
271 : 75 : ZX_FREE(cf->ctx, buf);
272 [ + + + - : 75 : D("SESSION CREATED sid(%s)", STRNULLCHK(ses->sid));
- + ]
273 : 75 : return 1;
274 : : }
275 : :
276 : : /*() Delete, or archive, session object from file system. Assertion, if any,
277 : : * is not deleted. This is called upon explicit logout events. However, in reality
278 : : * many sessions are simply abandoned, thus a deploying site should implement
279 : : * some mechanism, such as a cron(8) job to remove or archive expired sessions. */
280 : :
281 : : /* Called by: zxid_idp_dispatch, zxid_idp_slo_do, zxid_mgmt x3, zxid_simple_ses_active_cf x3, zxid_sp_dispatch, zxid_sp_slo_do */
282 : : int zxid_del_ses(zxid_conf* cf, zxid_ses* ses)
283 : 9 : {
284 : : char old[ZXID_MAX_BUF];
285 : : char new[ZXID_MAX_BUF];
286 : : int len;
287 [ + - + + ]: 9 : if (!ses || !ses->sid) {
288 [ + - - + ]: 2 : D("No session in place. %p", ses);
289 : 2 : return 0;
290 : : }
291 : :
292 [ + - ]: 7 : if (ses->sid) {
293 : 7 : len = strlen(ses->sid);
294 [ - + ]: 7 : if (len != strspn(ses->sid, safe_basis_64)) {
295 : 0 : ERR("EVIL Session ID(%s)", ses->sid);
296 : 0 : return 0;
297 : : }
298 : : }
299 : :
300 [ - + ]: 7 : if (!name_from_path(old, sizeof(old), "%s" ZXID_SES_DIR "%s", cf->path, ses->sid))
301 : 0 : return 0;
302 : :
303 [ - + ]: 7 : if (cf->ses_arch_dir) {
304 [ # # ]: 0 : if (!name_from_path(new, sizeof(new), "%s%s", cf->ses_arch_dir, ses->sid))
305 : 0 : return 0;
306 [ # # ]: 0 : if (rename(old,new) == -1) {
307 : 0 : perror("rename to archieve session");
308 : 0 : ERR("Deleting session by renaming failed old(%s) new(%s), euid=%d egid=%d", old, new, geteuid(), getegid());
309 : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "S", "EFILE", old, "ses arch rename, permissions?");
310 : 0 : return 0;
311 : : }
312 : : } else {
313 : : DIR* dir;
314 : : struct dirent * de;
315 : :
316 : 7 : dir = opendir(old);
317 [ - + ]: 7 : if (!dir) {
318 : 0 : perror("opendir to delete session");
319 : 0 : ERR("Deleting session by opendir failed old(%s), euid=%d egid=%d", old, geteuid(), getegid());
320 : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "S", "EFILE", old, "ses del opendir, permissions?");
321 : 0 : return 0;
322 : : }
323 [ + + ]: 45 : while (de = readdir(dir)) {
324 [ + + + + : 31 : if (de->d_name[0] == '.' && ONE_OF_2(de->d_name[1], '.', 0)) /* skip . and .. */
+ + ]
325 : : continue;
326 [ - + ]: 17 : if (!name_from_path(new, sizeof(new), "%s" ZXID_SES_DIR "%s/%s", cf->path, ses->sid, de->d_name))
327 : 0 : return 0;
328 [ - + ]: 17 : if (unlink(new) == -1) {
329 : 0 : perror("unlink to delete files in session");
330 : 0 : ERR("Deleting session file(%s) by unlink failed, euid=%d egid=%d", new, geteuid(), getegid());
331 : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "S", "EFILE", new, "ses unlink, permissions?");
332 : 0 : return 0;
333 : : }
334 : : }
335 : 7 : closedir(dir);
336 [ - + ]: 7 : if (rmdir(old) == -1) {
337 : 0 : perror("rmdir to delete session");
338 : 0 : ERR("Deleting session by rmdir failed old(%s), euid=%d egid=%d", old, geteuid(), getegid());
339 : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "S", "EFILE", old, "ses rmdir, permissions?");
340 : 0 : return 0;
341 : : }
342 : : }
343 : 7 : return 1;
344 : : }
345 : :
346 : : /*() Find a session object by a number of criteria.
347 : : *
348 : : * cf:: ZXID configuration object
349 : : * ses:: Result parameter. Must have been previously allocated. This will be modified
350 : : * to match the found session.
351 : : * ses_ix:: Session Index, usually from SSO asserion or from SLO request. If not
352 : : * supplied (i.e. 0), the ~nid~ MUST be supplied and will be used as sole basis for
353 : : * deleting the session.
354 : : * nid:: The idp assigned Name ID associated with the session. If supplied as 0, then
355 : : * ~ses_ix~ MUST be supplied and will be used to determine which session is deleted.
356 : : * return:: 0 unknown session or error, 1 session found successfully */
357 : :
358 : : /* Called by: zxid_idp_slo_do, zxid_sp_slo_do */
359 : : int zxid_find_ses(zxid_conf* cf, zxid_ses* ses, struct zx_str* ses_ix, struct zx_str* nid)
360 : 2 : {
361 : : char buf[ZXID_MAX_BUF];
362 : : DIR* dir;
363 : : struct dirent * de;
364 : :
365 [ + - - + : 2 : D("ses_ix(%.*s) nid(%.*s)", ses_ix?ses_ix->len:0, ses_ix?ses_ix->s:"", nid?nid->len:0, nid?nid->s:"");
- + - + -
+ - + ]
366 : :
367 [ - + ]: 2 : if (!name_from_path(buf, sizeof(buf), "%s" ZXID_SES_DIR, cf->path))
368 : 0 : return 0;
369 : :
370 : 2 : dir = opendir(buf);
371 [ - + ]: 2 : if (!dir) {
372 : 0 : perror("opendir to find session");
373 : 0 : ERR("Finding session by opendir failed buf(%s), euid=%d egid=%d", buf, geteuid(), getegid());
374 : 0 : return 0;
375 : : }
376 [ + - ]: 8 : while (de = readdir(dir)) {
377 [ + + + + : 6 : if (de->d_name[0] == '.' && ONE_OF_2(de->d_name[1], '.', 0)) /* skip . and .. */
- + ]
378 : : continue;
379 [ + - ]: 2 : if (zxid_get_ses(cf, ses, de->d_name)) {
380 [ - + # # : 2 : if (nid && (!ses->nid || memcmp(ses->nid, nid->s, nid->len) || ses->nid[nid->len]))
# # # # ]
381 : : continue;
382 [ - + # # : 2 : if (ses_ix && (!ses->sesix || memcmp(ses->sesix, ses_ix->s, ses_ix->len) || ses->sesix[ses_ix->len]))
# # # # ]
383 : : continue;
384 : 2 : return 1;
385 : : }
386 : : }
387 : 0 : closedir(dir);
388 : 0 : ZERO(ses, sizeof(zxid_ses));
389 : 0 : return 0;
390 : : }
391 : :
392 : : /* EOF -- zxidses.c */
|