Branch data Line data Source code
1 : : /* zxidslo.c - Handwritten functions for implementing Single LogOut logic for SP
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: zxidslo.c,v 1.42 2010-01-08 02:10:09 sampo Exp $
10 : : *
11 : : * 12.8.2006, created --Sampo
12 : : * 12.10.2007, tweaked for signing SLO and MNI --Sampo
13 : : * 14.4.2008, added SimpleSign --Sampo
14 : : * 7.10.2008, added documentation --Sampo
15 : : * 12.2.2010, added locking to lazy loading --Sampo
16 : : */
17 : :
18 : : #include "platform.h" /* needed on Win32 for pthread_mutex_lock() et al. */
19 : :
20 : : #include "errmac.h"
21 : : #include "zxid.h"
22 : : #include "zxidpriv.h"
23 : : #include "zxidconf.h"
24 : : #include "saml2.h"
25 : : #include "c/zx-const.h"
26 : : #include "c/zx-ns.h"
27 : : #include "c/zx-data.h"
28 : :
29 : : /* ============== Single Logout ============== */
30 : :
31 : : /*(i) SOAP client for sending Single Logout to IdP. The SOAP call is made
32 : : * using CURL HTTP Client and will block until response is received.
33 : : *
34 : : * return:: 1 if successful. 0 upon failure. */
35 : :
36 : : /* Called by: zxid_mgmt, zxid_simple_ses_active_cf */
37 : : int zxid_sp_slo_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
38 : 0 : {
39 : : X509* sign_cert;
40 : : EVP_PKEY* sign_pkey;
41 : :
42 : 0 : zxid_get_ses_sso_a7n(cf, ses);
43 [ # # ]: 0 : if (ses->a7n) {
44 : : struct zxsig_ref refs;
45 : : struct zx_root_s* r;
46 : : struct zx_e_Body_s* body;
47 : : struct zx_str* ses_ix;
48 : : zxid_entity* idp_meta;
49 : :
50 [ # # ]: 0 : ses_ix = ses->a7n->AuthnStatement?&ses->a7n->AuthnStatement->SessionIndex->g:0;
51 [ # # ]: 0 : if (cf->log_level>0)
52 [ # # # # : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "W", "SLOSOAP", ses->sid, "sesix(%.*s)", ses_ix?ses_ix->len:1, ses_ix?ses_ix->s:"?");
# # # # #
# ]
53 : :
54 : 0 : idp_meta = zxid_get_ses_idp(cf, ses);
55 [ # # ]: 0 : if (!idp_meta)
56 : 0 : return 0;
57 : :
58 : 0 : body = zx_NEW_e_Body(cf->ctx,0);
59 : 0 : body->LogoutRequest = zxid_mk_logout(cf, zxid_get_user_nameid(cf, ses->nameid), ses_ix, idp_meta);
60 [ # # ]: 0 : if (cf->sso_soap_sign) {
61 : 0 : ZERO(&refs, sizeof(refs));
62 : 0 : refs.id = &body->LogoutRequest->ID->g;
63 : 0 : refs.canon = zx_easy_enc_elem_sig(cf, &body->LogoutRequest->gg);
64 [ # # ]: 0 : if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert slo")) {
65 : 0 : body->LogoutRequest->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey);
66 : 0 : zx_add_kid_after_sa_Issuer(&body->LogoutRequest->gg, &body->LogoutRequest->Signature->gg);
67 : : }
68 : 0 : zx_str_free(cf->ctx, refs.canon);
69 : : }
70 : 0 : r = zxid_idp_soap(cf, cgi, ses, idp_meta, ZXID_SLO_SVC, body);
71 [ # # ]: 0 : if (!zxid_saml_ok(cf, cgi, r->Envelope->Body->LogoutResponse->Status, "LogoutResp"))
72 : 0 : return 0;
73 : 0 : return 1;
74 : : }
75 [ # # ]: 0 : if (ses->a7n11) {
76 : 0 : ERR("Not implemented, SAML 1.1 assetion %d", 0);
77 : : }
78 [ # # ]: 0 : if (ses->a7n12) {
79 : 0 : ERR("Not implemented, ID-FF 1.2 type SAML 1.1 assetion %d", 0);
80 : : }
81 : 0 : ERR("Session sid(%s) lacks SSO assertion.", ses->sid);
82 : 0 : return 0;
83 : : }
84 : :
85 : : /*(i) Send Single Logout to IdP using redirect binding. This function
86 : : * generates the URL encapsulating the request. You need to pass this
87 : : * URL to the appropriate function in your environment to provoke
88 : : * an HTTP 302 redirect.
89 : : *
90 : : * cf:: ZXID config object, also used for memory allocation
91 : : * cgi:: Data parsed from POST or query string. Provides parameters to determine
92 : : * details of the SLO request
93 : : * ses:: Session object. Used to determine session index (~ses_ix~) and name id, among others
94 : : * return:: location string if successful. "* ERR" upon failure. */
95 : :
96 : : /* Called by: zxid_mgmt, zxid_simple_ses_active_cf */
97 : : struct zx_str* zxid_sp_slo_redir(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
98 : 2 : {
99 : 2 : zxid_get_ses_sso_a7n(cf, ses);
100 [ + - ]: 2 : if (ses->a7n) {
101 : : struct zx_sp_LogoutRequest_s* r;
102 : : struct zx_str* rs;
103 : : struct zx_str* loc;
104 : : zxid_entity* idp_meta;
105 : : struct zx_str* ses_ix;
106 : :
107 [ + - ]: 2 : ses_ix = ses->a7n->AuthnStatement?&ses->a7n->AuthnStatement->SessionIndex->g:0;
108 [ + - ]: 2 : if (cf->log_level>0)
109 [ + - + - : 2 : zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "W", "SLOREDIR", ses->sid, "sesix(%.*s)", ses_ix?ses_ix->len:1, ses_ix?ses_ix->s:"?");
+ - + - +
- ]
110 : :
111 : 2 : idp_meta = zxid_get_ses_idp(cf, ses);
112 [ - + ]: 2 : if (!idp_meta)
113 : 0 : return zx_dup_str(cf->ctx, "* ERR");
114 : :
115 : 2 : loc = zxid_idp_loc(cf, cgi, ses, idp_meta, ZXID_SLO_SVC, SAML2_REDIR);
116 [ - + ]: 2 : if (!loc)
117 : 0 : return zx_dup_str(cf->ctx, "* ERR");
118 : 2 : r = zxid_mk_logout(cf, zxid_get_user_nameid(cf, ses->nameid), ses_ix, idp_meta);
119 : 2 : r->Destination = zx_ref_len_attr(cf->ctx, &r->gg, zx_Destination_ATTR, loc->len, loc->s);
120 : 2 : rs = zx_easy_enc_elem_opt(cf, &r->gg);
121 [ + - - + ]: 2 : D("SLO(%.*s)", rs->len, rs->s);
122 : 2 : return zxid_saml2_redir(cf, loc, rs, 0);
123 : : }
124 [ # # ]: 0 : if (ses->a7n11) {
125 : 0 : ERR("Not implemented, SAML 1.1 assetion %d", 0);
126 : : }
127 [ # # ]: 0 : if (ses->a7n12) {
128 : 0 : ERR("Not implemented, ID-FF 1.2 type SAML 1.1 assetion %d", 0);
129 : : }
130 : 0 : ERR("Session sid(%s) lacks SSO assertion.", ses->sid);
131 : 0 : return zx_dup_str(cf->ctx, "* ERR");
132 : : }
133 : :
134 : : /*() Generate SLO Response, SP or IdP variant. The actual session invalidation must be
135 : : * done somewhere else, i.e. this is just the final protocol phase of the SLO. */
136 : :
137 : : /* Called by: zxid_idp_dispatch, zxid_sp_dispatch */
138 : : struct zx_str* zxid_slo_resp_redir(zxid_conf* cf, zxid_cgi* cgi, struct zx_sp_LogoutRequest_s* req)
139 : 2 : {
140 : : struct zx_sp_LogoutResponse_s* res;
141 : : zxid_entity* meta;
142 : : struct zx_str* loc;
143 : : struct zx_str* ss;
144 : : struct zx_str* ss2;
145 : :
146 [ + - + - : 2 : meta = zxid_get_ent_ss(cf, ZX_GET_CONTENT(req->Issuer));
+ - ]
147 : 2 : loc = zxid_idp_loc_raw(cf, cgi, meta, ZXID_SLO_SVC, SAML2_REDIR, 0);
148 [ + - ]: 2 : if (!loc)
149 : 2 : loc = zxid_sp_loc_raw(cf, cgi, meta, ZXID_SLO_SVC, SAML2_REDIR, 0);
150 [ - + ]: 2 : if (!loc)
151 : 0 : return zx_dup_str(cf->ctx, "* ERR"); /* *** consider sending error page */
152 : :
153 : 2 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "SLORESREDIR", 0, "");
154 : :
155 : 2 : res = zxid_mk_logout_resp(cf, zxid_OK(cf, 0), &req->ID->g);
156 : 2 : res->Destination = zx_ref_len_attr(cf->ctx, &res->gg, zx_Destination_ATTR, loc->len, loc->s);
157 : 2 : ss = zx_easy_enc_elem_opt(cf, &res->gg);
158 : 2 : ss2 = zxid_saml2_resp_redir(cf, loc, ss, cgi->rs);
159 : : /*zx_str_free(cf->ctx, loc); Do NOT free loc as it is still referenced by the metadata. */
160 : 2 : zx_str_free(cf->ctx, ss);
161 : 2 : return ss2;
162 : : }
163 : :
164 : : /*() Process SP SLO request. */
165 : :
166 : : /* Called by: zxid_idp_dispatch, zxid_sp_dispatch, zxid_sp_soap_dispatch */
167 : : int zxid_sp_slo_do(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_sp_LogoutRequest_s* req)
168 : 0 : {
169 [ # # # # : 0 : struct zx_str* sesix = ZX_GET_CONTENT(req->SessionIndex);
# # ]
170 : :
171 [ # # ]: 0 : if (!zxid_chk_sig(cf, cgi, ses, &req->gg, req->Signature, req->Issuer, 0, "LogoutRequest"))
172 : 0 : return 0;
173 : :
174 [ # # ]: 0 : if (cf->log_level>0)
175 [ # # # # : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), cgi->sigval, "K", "SLO", ses->sid, "sesix(%.*s)", sesix?sesix->len:1, sesix?sesix->s:"?");
# # # # #
# ]
176 : :
177 : 0 : req->NameID = zxid_decrypt_nameid(cf, req->NameID, req->EncryptedID);
178 [ # # # # : 0 : if (!ZX_GET_CONTENT(req->NameID)) {
# # ]
179 : 0 : ERR("SLO failed: request does not have NameID. %p", req->NameID);
180 : 0 : return 0;
181 : : }
182 [ # # # # : 0 : zxid_find_ses(cf, ses, sesix, ZX_GET_CONTENT(req->NameID));
# # ]
183 : 0 : zxid_del_ses(cf, ses);
184 : 0 : return 1;
185 : : }
186 : :
187 : : /*() Process IdP SLO request. The IdP SLO Requests are complicated by the need
188 : : * to log the user out of other SPs as well, if they belong to same session.
189 : : * Part of the complication is figuring out what constitutes "same session".
190 : : * Finally, the redirect profiles may be "hairy" to handle if some SP does
191 : : * not collaborate in the SLO. For SOAP similar problem exists, but it should be
192 : : * manageable. */
193 : :
194 : : /* Called by: zxid_idp_dispatch, zxid_idp_soap_dispatch, zxid_sp_dispatch */
195 : : int zxid_idp_slo_do(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_sp_LogoutRequest_s* req)
196 : 2 : {
197 [ + - + - : 2 : struct zx_str* sesix = ZX_GET_CONTENT(req->SessionIndex);
+ - ]
198 [ + - ]: 2 : if (sesix)
199 [ + - + - : 2 : sesix = zxid_psobj_dec(cf, ZX_GET_CONTENT(req->Issuer), "ZS", sesix);
+ - ]
200 : :
201 [ - + ]: 2 : if (!zxid_chk_sig(cf, cgi, ses, &req->gg, req->Signature, req->Issuer, 0, "LogoutRequest"))
202 : 0 : return 0;
203 : :
204 [ + - ]: 2 : if (cf->log_level>0)
205 [ - + - + : 2 : zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), cgi->sigval, "K", "ISLO", ses->sid, "sesix(%.*s)", sesix?sesix->len:1, sesix?sesix->s:"?");
- + # # #
# ]
206 [ + - ]: 2 : if (cf->loguser)
207 [ - + - + : 2 : zxlogusr(cf, ses->uid, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), cgi->sigval, "K", "ISLO", ses->sid, "sesix(%.*s)", sesix?sesix->len:1, sesix?sesix->s:"?");
- + # # #
# ]
208 : :
209 : 2 : req->NameID = zxid_decrypt_nameid(cf, req->NameID, req->EncryptedID);
210 [ + - + - : 2 : if (!ZX_GET_CONTENT(req->NameID)) {
- + ]
211 [ # # # # ]: 0 : INFO("SLO: request does not have NameID. %p sesix(%.*s)", req->NameID, sesix?sesix->len:0, sesix?sesix->s:"");
212 : : }
213 [ + - ]: 2 : if (zxid_find_ses(cf, ses, sesix, 0 /*ZX_GET_CONTENT(req->NameID)*/))
214 : 2 : zxid_del_ses(cf, ses);
215 : 2 : return 1;
216 : : }
217 : :
218 : : /* EOF -- zxidslo.c */
|