1 /* zxidmni.c  -  Handwritten functions for NameID Management 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: zxidmni.c,v 1.10 2010-01-08 02:10:09 sampo Exp $
10  *
11  * 12.10.2007, split from zxidslo.c --Sampo
12  * 7.10.2008,  added documentation --Sampo
13  * 12.2.2010,  added locking to lazy loading --Sampo
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 /* ============== MNI / NID Mgmt / Defederation ============== */
27 
28 /*() Change SPNameID (newnym supplied), or Terminate federation (newnym not supplied),
29  * using SAML2 SOAP binding. This is the (SP) client side that contacts the IdP. */
30 
31 /* Called by:  a7n_test, zxid_mgmt, zxid_simple_ses_active_cf */
zxid_sp_mni_soap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zx_str * new_nym)32 int zxid_sp_mni_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_str* new_nym)
33 {
34   X509* sign_cert;
35   EVP_PKEY* sign_pkey;
36 
37   zxid_get_ses_sso_a7n(cf, ses);
38   if (ses->a7n) {
39     struct zxsig_ref refs;
40     struct zx_root_s* r;
41     struct zx_e_Body_s* body;
42     zxid_entity* idp_meta;
43 
44     if (cf->log_level>0)
45       zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "W", "MNISOAP", ses->sid, "newnym(%.*s) loc", new_nym?new_nym->len:0, new_nym?new_nym->s:"");
46 
47     idp_meta = zxid_get_ses_idp(cf, ses);
48     if (!idp_meta)
49       return 0;
50 
51     body = zx_NEW_e_Body(cf->ctx,0);
52     body->ManageNameIDRequest = zxid_mk_mni(cf, zxid_get_user_nameid(cf, ses->nameid), new_nym, idp_meta);
53     if (cf->sso_soap_sign) {
54       ZERO(&refs, sizeof(refs));
55       refs.id = &body->ManageNameIDRequest->ID->g;
56       refs.canon = zx_easy_enc_elem_sig(cf, &body->ManageNameIDRequest->gg);
57       if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert mni")) {
58 	body->ManageNameIDRequest->Signature
59 	  = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
60 	zx_add_kid_after_sa_Issuer(&body->ManageNameIDRequest->gg, &body->ManageNameIDRequest->Signature->gg);
61       }
62       zx_str_free(cf->ctx, refs.canon);
63     }
64     r = zxid_idp_soap(cf, cgi, ses, idp_meta, ZXID_MNI_SVC, body);
65     if (!zxid_saml_ok(cf, cgi, r->Envelope->Body->ManageNameIDResponse->Status, "MniResp"))
66       return 0;
67     /* *** Take actual steps to terminate the federation or change the name IDs */
68     return 1;
69   }
70   if (ses->a7n11) {
71     ERR("Not implemented, SAML 1.1 assetion %d", 0);
72   }
73   if (ses->a7n12) {
74     ERR("Not implemented, ID-FF 1.2 type SAML 1.1 assetion %d", 0);
75   }
76   ERR("Session sid(%s) lacks SSO assertion.", ses->sid);
77   return 0;
78 }
79 
80 /*() Change SPNameID (newnym supplied), or Terminate federation (newnym not supplied),
81  * using SAML2 HTTP redirect binding. This is the (SP) client side that contacts the IdP.
82  * Return the HTTP 302 redirect LOCATION header + CRLF2. Returns the URL as string to which
83  * the environment should cause the user (browser) to be redirected. */
84 
85 /* Called by:  zxid_mgmt, zxid_simple_ses_active_cf */
zxid_sp_mni_redir(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zx_str * new_nym)86 struct zx_str* zxid_sp_mni_redir(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_str* new_nym)
87 {
88   zxid_get_ses_sso_a7n(cf, ses);
89   if (ses->a7n) {
90     struct zx_sp_ManageNameIDRequest_s* r;
91     struct zx_str* rs;
92     struct zx_str* loc;
93     zxid_entity* idp_meta;
94 
95     if (cf->log_level>0)
96       zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "W", "MNIREDIR", ses->sid, "newnym(%.*s)", new_nym?new_nym->len:0, new_nym?new_nym->s:"");
97 
98     idp_meta = zxid_get_ses_idp(cf, ses);
99     if (!idp_meta)
100       return zx_dup_str(cf->ctx, "* ERR");
101 
102     loc = zxid_idp_loc(cf, cgi, ses, idp_meta, ZXID_MNI_SVC, SAML2_REDIR);
103     if (!loc)
104       return zx_dup_str(cf->ctx, "* ERR");
105     r = zxid_mk_mni(cf, zxid_get_user_nameid(cf, ses->nameid), new_nym, 0);
106     r->Destination = zx_ref_len_attr(cf->ctx, &r->gg, zx_Destination_ATTR, loc->len, loc->s);
107     rs = zx_easy_enc_elem_opt(cf, &r->gg);
108     D("NIReq(%.*s)", rs->len, rs->s);
109     return zxid_saml2_redir(cf, loc, rs, 0);
110   }
111   if (ses->a7n11) {
112     ERR("Not implemented, SAML 1.1 assetion %d", 0);
113   }
114   if (ses->a7n12) {
115     ERR("Not implemented, ID-FF 1.2 type SAML 1.1 assetion %d", 0);
116   }
117   ERR("Session sid(%s) lacks SSO assertion.", ses->sid);
118   return zx_dup_str(cf->ctx, "* ERR");
119 }
120 
121 /*() Process <ManageNameIDRequest>, presumably received from IdP. This is very rarely
122  * used. */
123 
124 /* Called by:  zxid_idp_soap_dispatch, zxid_mni_do_ss, zxid_sp_soap_dispatch */
zxid_mni_do(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zx_sp_ManageNameIDRequest_s * mni)125 struct zx_sp_ManageNameIDResponse_s* zxid_mni_do(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_sp_ManageNameIDRequest_s* mni)
126 {
127   zxid_nid* nid;
128   struct zx_str* newnym;
129 
130   if (!zxid_chk_sig(cf, cgi, ses, &mni->gg, mni->Signature, mni->Issuer, 0, "ManageNameIDRequest"))
131     return 0;
132 
133   nid = zxid_decrypt_nameid(cf, mni->NameID, mni->EncryptedID);
134   if (!ZX_GET_CONTENT(nid)) {
135     ERR("MNI failed: request does not have NameID. %p", nid);
136     return 0;
137   }
138 
139   newnym = zxid_decrypt_newnym(cf, ZX_GET_CONTENT(mni->NewID), mni->NewEncryptedID);
140   if (!newnym) {
141     D("MNI Terminate %d",0);
142   } else {
143     D("MNI Change newnym(%.*s)", newnym->len, newnym->s);
144     zxid_user_change_nameid(cf, nid, newnym);
145   }
146   return zxid_mk_mni_resp(cf, zxid_OK(cf,0), &mni->ID->g);
147 }
148 
149 /*() Wrapper for zxid_mni_do(), which see. */
150 
151 /* Called by:  a7n_test, zxid_idp_dispatch, zxid_sp_dispatch */
zxid_mni_do_ss(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zx_sp_ManageNameIDRequest_s * mni,struct zx_str * loc)152 struct zx_str* zxid_mni_do_ss(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_sp_ManageNameIDRequest_s* mni, struct zx_str* loc)
153 {
154   struct zx_sp_ManageNameIDResponse_s* res;
155   res = zxid_mk_mni_resp(cf, zxid_OK(cf,0), &mni->ID->g);
156   res = zxid_mni_do(cf, cgi, ses, mni);
157   res->Destination = zx_ref_len_attr(cf->ctx, &res->gg, zx_Destination_ATTR, loc->len, loc->s);
158   return zx_easy_enc_elem_opt(cf, &res->gg);
159 }
160 
161 /* EOF  --  zxidmni.c */
162