1 /* zxididpx.c  -  Handwritten functions for IdP dispatch
2  * Copyright (c) 2008-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3  * This is confidential unpublished proprietary source code of the author.
4  * NO WARRANTY, not even implied warranties. Contains trade secrets.
5  * Distribution prohibited unless authorized in writing.
6  * Licensed under Apache License 2.0, see file COPYING.
7  * $Id: zxididpx.c,v 1.10 2010-01-08 02:10:09 sampo Exp $
8  *
9  * 14.11.2008,  created --Sampo
10  * 12.2.2010,   added locking to lazy loading --Sampo
11  * 11.12.2011,  added OAUTH2 and OpenID-Connect support --Sampo
12  *
13  * TODO: *** Review of all of IdP SLO and MNI code
14  */
15 
16 #include "platform.h"
17 #include "errmac.h"
18 #include "zxid.h"
19 #include "zxidpriv.h"
20 #include "zxidconf.h"
21 #include "saml2.h"
22 #include "c/zx-const.h"
23 #include "c/zx-ns.h"
24 #include "c/zx-data.h"
25 
26 /* ============== Dispatch incoming requests and responses ============== */
27 
28 /*() Dispatch redirect and post binding requests.
29  *
30  * return:: a string (such as Location: header) and let the caller output it. */
31 
32 /* Called by:  zxid_simple_ses_active_cf x2 */
zxid_idp_dispatch(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,int chk_dup)33 struct zx_str* zxid_idp_dispatch(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int chk_dup)
34 {
35   struct zx_sp_LogoutRequest_s* req;
36   zxid_entity* sp_meta;
37   struct zx_str* loc;
38   struct zx_str* ss;
39   struct zx_str* ss2;
40   struct zx_root_s* r;
41   ses->sigres = ZXSIG_NO_SIG;
42 
43   if (cgi->response_type)  /* OAUTH2 / OpenID-Connect */
44     return zxid_oauth2_az_server_sso(cf, cgi, ses);
45 
46   r = zxid_decode_redir_or_post(cf, cgi, ses, chk_dup);
47   if (!r)
48     return zx_dup_str(cf->ctx, "* ERR");
49 
50   if (r->AuthnRequest)
51     return zxid_idp_sso(cf, cgi, ses, r->AuthnRequest);
52 
53   if (req = r->LogoutRequest) {
54     D("IdP SLO %d", 0);
55     if (cf->idp_ena) {  /* *** Kludgy check */
56       if (!zxid_idp_slo_do(cf, cgi, ses, req))
57 	return zx_dup_str(cf->ctx, "* ERR");
58     } else {
59       if (!zxid_sp_slo_do(cf, cgi, ses, req))
60 	return zx_dup_str(cf->ctx, "* ERR");
61     }
62     /* *** Need to do much more to log out all other SPs of the session. */
63     return zxid_slo_resp_redir(cf, cgi, req);
64   }
65 
66   if (r->LogoutResponse) {
67     if (!zxid_saml_ok(cf, cgi, r->LogoutResponse->Status, "SLO resp"))
68       return zx_dup_str(cf->ctx, "* ERR");
69     cgi->msg = "Logout Response OK. Logged out.";
70     zxid_del_ses(cf, ses);
71     return zx_dup_str(cf->ctx, "K"); /* Prevent mgmt screen from displaying, show login screen. */
72   }
73 
74   if (r->ManageNameIDRequest) {
75     sp_meta = zxid_get_ent_ss(cf, ZX_GET_CONTENT(r->ManageNameIDRequest->Issuer));
76     loc = zxid_sp_loc_raw(cf, cgi, sp_meta, ZXID_MNI_SVC, SAML2_REDIR, 0);
77     if (!loc)
78       return 0;  /* *** consider sending error page */
79     ss = zxid_mni_do_ss(cf, cgi, ses, r->ManageNameIDRequest, loc);
80     ss2 = zxid_saml2_resp_redir(cf, loc, ss, cgi->rs);
81     zx_str_free(cf->ctx, loc);
82     zx_str_free(cf->ctx, ss);
83     return ss2;
84   }
85 
86   if (r->ManageNameIDResponse) {
87     if (!zxid_saml_ok(cf, cgi, r->ManageNameIDResponse->Status, "MNI resp")) {
88       ERR("MNI Response indicates failure. %d", 0);
89       return zx_dup_str(cf->ctx, "* ERR");
90     }
91     cgi->msg = "Manage NameID Response OK.";
92     return zx_dup_str(cf->ctx, "M"); /* Defederation doesn't have to mean SLO, show mgmt screen. */
93   }
94 
95   if (cf->log_level > 0)
96     zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "C", "IDPDISP", 0, "sid(%s) unknown req or resp (loc)", ses->sid);
97   ERR("Unknown request or response %p", r);
98   return zx_dup_str(cf->ctx, "* ERR");
99 }
100 
101 #if 0
102 /*(-) SOAP dispatch can also handle requests and responses received via artifact
103  * resolution. However only some combinations make sense.
104  * Return 0 for failure, otherwise some success code such as ZXID_SSO_OK
105  * *** NOT CALLED FROM ANYWHERE. See zxid_sp_soap_dispatch() for real action */
106 
107 /* Called by: */
108 int zxid_idp_soap_dispatch(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_root_s* r)
109 {
110   X509* sign_cert;
111   RSA*  sign_pkey;
112   struct zxsig_ref refs;
113   struct zx_e_Body_s* body;
114   struct zx_sp_LogoutRequest_s* req;
115   ses->sigres = ZXSIG_NO_SIG;
116 
117   if (!r) goto bad;
118   if (!r->Envelope) goto bad;
119 
120   if (cf->log_level > 1)
121     zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "W", "IDPDISP", 0, "sid(%s) soap", ses->sid);
122 
123   if (r->Envelope->Body->ArtifactResolve) {
124     D("ArtifactResolve not implemented yet %d",0);
125     //if (!zxid_saml_ok(cf, cgi, r->Envelope->Body->ArtifactResponse->Status, "ArtResp"))
126     //  return 0;
127     //return zxid_sp_dig_sso_a7n(cf, cgi, ses, r->Envelope->Body->ArtifactResponse->Response);
128   }
129 
130   if (req = r->Envelope->Body->LogoutRequest) {
131     if (!zxid_idp_slo_do(cf, cgi, ses, req))
132       return 0;
133 
134     body = zx_NEW_e_Body(cf->ctx,0);
135     body->LogoutResponse = zxid_mk_logout_resp(cf, zxid_OK(cf), req->ID);
136     if (cf->sso_soap_resp_sign) {
137       ZERO(&refs, sizeof(refs));
138       refs.id = body->LogoutResponse->ID;
139       refs.canon = zx_EASY_ENC_SO_sp_LogoutResponse(cf->ctx, body->LogoutResponse);
140       if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert idp slo")) {
141 	body->LogoutResponse->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
142 	zx_add_kid_after_sa_Issuer(&body->LogoutResponse->gg, &body->LogoutResponse->Signature->gg);
143       }
144       zx_str_free(cf->ctx, refs.canon);
145     }
146     return zxid_soap_cgi_resp_body(cf, ses, body);
147   }
148 
149   if (r->Envelope->Body->ManageNameIDRequest) {
150     struct zx_sp_ManageNameIDResponse_s* res = zxid_mni_do(cf, cgi, ses, r->Envelope->Body->ManageNameIDRequest);
151     body = zx_NEW_e_Body(cf->ctx,0);
152     body->ManageNameIDResponse = res;
153     if (cf->sso_soap_resp_sign) {
154       ZERO(&refs, sizeof(refs));
155       refs.id = res->ID;
156       refs.canon = zx_EASY_ENC_SO_sp_ManageNameIDResponse(cf->ctx, res);
157       if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert idp mni")) {
158 	res->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
159 	zx_add_kid_after_sa_Issuer(&res->gg, &res->Signature->gg);
160       }
161       zx_str_free(cf->ctx, refs.canon);
162     }
163     return zxid_soap_cgi_resp_body(cf, body, ZX_GET_CONTENT(r->Envelope->Body->ManageNameIDRequest->Issuer));
164   }
165 
166  bad:
167   ERR("Unknown soap request %p", r);
168   if (cf->log_level > 0)
169     zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "C", "IDPDISP", 0, "sid(%s) unknown soap req", ses->sid);
170   return 0;
171 }
172 
173 /*() Return 0 for failure, otherwise some success code such as ZXID_SSO_OK */
174 
175 /* Called by: */
176 int zxid_idp_soap_parse(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int len, char* buf)
177 {
178   struct zx_root_s* r;
179   r = zx_dec_zx_root(cf->ctx, len, buf, "idp soap parse");
180   if (!r || !r->Envelope || !r->Envelope->Body) {
181     ERR("Failed to parse SOAP request buf(%.*s)", len, buf);
182     zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "C", "BADXML", 0, "sid(%s) bad soap req", ses->sid);
183     return 0;
184   }
185   return zxid_sp_soap_dispatch(cf, cgi, ses, r);
186 }
187 #endif
188 
189 /* EOF  --  zxididpx.c */
190