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 */
zxid_sp_slo_soap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses)37 int zxid_sp_slo_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
38 {
39   X509* sign_cert;
40   EVP_PKEY* sign_pkey;
41 
42   zxid_get_ses_sso_a7n(cf, ses);
43   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     ses_ix = ses->a7n->AuthnStatement?&ses->a7n->AuthnStatement->SessionIndex->g:0;
51     if (cf->log_level>0)
52       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     idp_meta = zxid_get_ses_idp(cf, ses);
55     if (!idp_meta)
56       return 0;
57 
58     body = zx_NEW_e_Body(cf->ctx,0);
59     body->LogoutRequest = zxid_mk_logout(cf, zxid_get_user_nameid(cf, ses->nameid), ses_ix, idp_meta);
60     if (cf->sso_soap_sign) {
61       ZERO(&refs, sizeof(refs));
62       refs.id = &body->LogoutRequest->ID->g;
63       refs.canon = zx_easy_enc_elem_sig(cf, &body->LogoutRequest->gg);
64       if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert slo")) {
65 	body->LogoutRequest->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
66 	zx_add_kid_after_sa_Issuer(&body->LogoutRequest->gg, &body->LogoutRequest->Signature->gg);
67       }
68       zx_str_free(cf->ctx, refs.canon);
69     }
70     r = zxid_idp_soap(cf, cgi, ses, idp_meta, ZXID_SLO_SVC, body);
71     if (!zxid_saml_ok(cf, cgi, r->Envelope->Body->LogoutResponse->Status, "LogoutResp"))
72       return 0;
73     return 1;
74   }
75   if (ses->a7n11) {
76     ERR("Not implemented, SAML 1.1 assetion %d", 0);
77   }
78   if (ses->a7n12) {
79     ERR("Not implemented, ID-FF 1.2 type SAML 1.1 assetion %d", 0);
80   }
81   ERR("Session sid(%s) lacks SSO assertion.", ses->sid);
82   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 */
zxid_sp_slo_redir(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses)97 struct zx_str* zxid_sp_slo_redir(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
98 {
99   zxid_get_ses_sso_a7n(cf, ses);
100   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     ses_ix = ses->a7n->AuthnStatement?&ses->a7n->AuthnStatement->SessionIndex->g:0;
108     if (cf->log_level>0)
109       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     idp_meta = zxid_get_ses_idp(cf, ses);
112     if (!idp_meta)
113       return zx_dup_str(cf->ctx, "* ERR");
114 
115     loc = zxid_idp_loc(cf, cgi, ses, idp_meta, ZXID_SLO_SVC, SAML2_REDIR);
116     if (!loc)
117       return zx_dup_str(cf->ctx, "* ERR");
118     r = zxid_mk_logout(cf, zxid_get_user_nameid(cf, ses->nameid), ses_ix, idp_meta);
119     r->Destination = zx_ref_len_attr(cf->ctx, &r->gg, zx_Destination_ATTR, loc->len, loc->s);
120     rs = zx_easy_enc_elem_opt(cf, &r->gg);
121     D("SLO(%.*s)", rs->len, rs->s);
122     return zxid_saml2_redir(cf, loc, rs, 0);
123   }
124   if (ses->a7n11) {
125     ERR("Not implemented, SAML 1.1 assetion %d", 0);
126   }
127   if (ses->a7n12) {
128     ERR("Not implemented, ID-FF 1.2 type SAML 1.1 assetion %d", 0);
129   }
130   ERR("Session sid(%s) lacks SSO assertion.", ses->sid);
131   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 */
zxid_slo_resp_redir(zxid_conf * cf,zxid_cgi * cgi,struct zx_sp_LogoutRequest_s * req)138 struct zx_str* zxid_slo_resp_redir(zxid_conf* cf, zxid_cgi* cgi, struct zx_sp_LogoutRequest_s* req)
139 {
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   meta = zxid_get_ent_ss(cf, ZX_GET_CONTENT(req->Issuer));
147   loc = zxid_idp_loc_raw(cf, cgi, meta, ZXID_SLO_SVC, SAML2_REDIR, 0);
148   if (!loc)
149     loc = zxid_sp_loc_raw(cf, cgi, meta, ZXID_SLO_SVC, SAML2_REDIR, 0);
150   if (!loc)
151     return zx_dup_str(cf->ctx, "* ERR");  /* *** consider sending error page */
152 
153   zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "SLORESREDIR", 0, "");
154 
155   res = zxid_mk_logout_resp(cf, zxid_OK(cf, 0), &req->ID->g);
156   res->Destination = zx_ref_len_attr(cf->ctx, &res->gg, zx_Destination_ATTR, loc->len, loc->s);
157   ss = zx_easy_enc_elem_opt(cf, &res->gg);
158   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   zx_str_free(cf->ctx, ss);
161   return ss2;
162 }
163 
164 /*() Process SP SLO request. */
165 
166 /* Called by:  zxid_idp_dispatch, zxid_sp_dispatch, zxid_sp_soap_dispatch */
zxid_sp_slo_do(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zx_sp_LogoutRequest_s * req)167 int zxid_sp_slo_do(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_sp_LogoutRequest_s* req)
168 {
169   struct zx_str* sesix = ZX_GET_CONTENT(req->SessionIndex);
170 
171   if (!zxid_chk_sig(cf, cgi, ses, &req->gg, req->Signature, req->Issuer, 0, "LogoutRequest"))
172     return 0;
173 
174   if (cf->log_level>0)
175     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   req->NameID = zxid_decrypt_nameid(cf, req->NameID, req->EncryptedID);
178   if (!ZX_GET_CONTENT(req->NameID)) {
179     ERR("SLO failed: request does not have NameID. %p", req->NameID);
180     return 0;
181   }
182   zxid_find_ses(cf, ses, sesix, ZX_GET_CONTENT(req->NameID));
183   zxid_del_ses(cf, ses);
184   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 */
zxid_idp_slo_do(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zx_sp_LogoutRequest_s * req)195 int zxid_idp_slo_do(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_sp_LogoutRequest_s* req)
196 {
197   struct zx_str* sesix = ZX_GET_CONTENT(req->SessionIndex);
198   if (sesix)
199     sesix = zxid_psobj_dec(cf, ZX_GET_CONTENT(req->Issuer), "ZS", sesix);
200 
201   if (!zxid_chk_sig(cf, cgi, ses, &req->gg, req->Signature, req->Issuer, 0, "LogoutRequest"))
202     return 0;
203 
204   if (cf->log_level>0)
205     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   if (cf->loguser)
207     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   req->NameID = zxid_decrypt_nameid(cf, req->NameID, req->EncryptedID);
210   if (!ZX_GET_CONTENT(req->NameID)) {
211     INFO("SLO: request does not have NameID. %p sesix(%.*s)", req->NameID, sesix?sesix->len:0, sesix?sesix->s:"");
212   }
213   if (zxid_find_ses(cf, ses, sesix, 0 /*ZX_GET_CONTENT(req->NameID)*/))
214     zxid_del_ses(cf, ses);
215   return 1;
216 }
217 
218 /* EOF  --  zxidslo.c */
219