-
Notifications
You must be signed in to change notification settings - Fork 2
/
zxiddec.c
259 lines (237 loc) · 9.63 KB
/
zxiddec.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* zxiddec.c - Handwritten functions for Decoding Redirect or POST bindings
* Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
* Copyright (c) 2006-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
* Author: Sampo Kellomaki (sampo@iki.fi)
* This is confidential unpublished proprietary source code of the author.
* NO WARRANTY, not even implied warranties. Contains trade secrets.
* Distribution prohibited unless authorized in writing.
* Licensed under Apache License 2.0, see file COPYING.
* $Id: zxiddec.c,v 1.10 2010-01-08 02:10:09 sampo Exp $
*
* 12.8.2006, created --Sampo
* 12.10.2007, tweaked for signing SLO and MNI --Sampo
* 14.4.2008, added SimpleSign --Sampo
* 7.10.2008, added documentation --Sampo
* 10.3.2010, added predecode support --Sampo
*/
#include "platform.h" /* needed on Win32 for pthread_mutex_lock() et al. */
#include "errmac.h"
#include "zxid.h"
#include "zxidpriv.h"
#include "zxidutil.h"
#include "zxidconf.h"
#include "saml2.h"
#include "c/zx-const.h"
#include "c/zx-ns.h"
#include "c/zx-data.h"
/*() Look for issuer in all messages we support. */
/* Called by: zxid_decode_redir_or_post, zxid_simple_idp_show_an */
struct zx_sa_Issuer_s* zxid_extract_issuer(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_root_s* r)
{
struct zx_sa_Issuer_s* issuer = 0;
if (r->Response) issuer = r->Response->Issuer;
else if (r->AuthnRequest) issuer = r->AuthnRequest->Issuer;
else if (r->LogoutRequest) issuer = r->LogoutRequest->Issuer;
else if (r->LogoutResponse) issuer = r->LogoutResponse->Issuer;
else if (r->ManageNameIDRequest) issuer = r->ManageNameIDRequest->Issuer;
else if (r->ManageNameIDResponse) issuer = r->ManageNameIDResponse->Issuer;
else {
ERR("Unknown message type in redirect, post, or simple sign binding %d", 0);
cgi->sigval = "I";
cgi->sigmsg = "Unknown message type (SimpleSign, Redir, or POST).";
ses->sigres = ZXSIG_NO_SIG;
return 0;
}
if (!issuer) {
ERR("Missing issuer in redirect, post, or simple sign binding %d", 0);
cgi->sigval = "I";
cgi->sigmsg = "Issuer not found (SimpleSign, Redir, or POST).";
ses->sigres = ZXSIG_NO_SIG;
return 0;
}
return issuer;
}
/*(i) Decode redirect or POST binding message. zxid_saml2_redir_enc()
* performs the opposite operation. chk_dup is really flags
* 0x01 = Check dup
* 0x02 = Avoid sig check and logging
* See: */
/* Called by: zxid_idp_dispatch, zxid_simple_idp_show_an, zxid_sp_dispatch */
struct zx_root_s* zxid_decode_redir_or_post(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int chk_dup)
{
struct zx_sa_Issuer_s* issuer = 0;
zxid_entity* meta;
struct zx_str* ss;
struct zx_str* logpath;
struct zx_root_s* r = 0;
struct zx_str id_ss;
char id_buf[28];
char sigbuf[512]; /* 192 should be large enough for 1024bit RSA keys */
int simplesig = 0;
int msglen, len;
char* p;
char* m2;
char* p2;
char* msg;
char* b64msg;
char* field;
if (cgi->saml_resp && *cgi->saml_resp) {
field = "SAMLResponse";
b64msg = cgi->saml_resp;
} else if (cgi->saml_req && *cgi->saml_req) {
field = "SAMLRequest";
b64msg = cgi->saml_req;
} else {
ERR("No SAMLRequest or SAMLResponse field?! %p", cgi);
return 0;
}
msglen = strlen(b64msg);
msg = ZX_ALLOC(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(msglen));
p = unbase64_raw(b64msg, b64msg + msglen, msg, zx_std_index_64);
*p = 0;
DD("Msg(%s) x=%x", msg, *msg);
/* Skip whitespace in the beginning and end of the payload to help correct POST detection. */
for (m2 = msg; m2 < p; ++m2)
if (!ONE_OF_4(*m2, ' ', '\t', '\015', '\012'))
break;
for (p2 = p-1; m2 < p2; --p2)
if (!ONE_OF_4(*p2, ' ', '\t', '\015', '\012'))
break;
DD("Msg_sans_ws(%.*s) start=%x end=%x", p2-m2+1, m2, *m2, *p2);
if (!(chk_dup & 0x02) && cf->log_level > 1)
zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "W", "REDIRDEC", 0, "sid(%s) len=%d", STRNULLCHK(ses->sid), msglen);
if (*m2 == '<' && *p2 == '>') { /* POST profiles do not compress the payload */
len = p2 - m2 + 1;
p = m2;
simplesig = 1;
} else {
D("Detected compressed payload. [[m2(%c) %x p2(%c) %x]]", *m2, *m2, *p2, *p2);
p = zx_zlib_raw_inflate(cf->ctx, p-msg, msg, &len); /* Redir uses compressed payload. */
ZX_FREE(cf->ctx, msg);
}
r = zx_dec_zx_root(cf->ctx, len, p, "decode redir or post");
if (!r) {
ERR("Failed to parse redir buf(%.*s)", len, p);
zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "C", "BADXML", 0, "sid(%s) bad redir", STRNULLCHK(ses->sid));
return 0;
}
if (chk_dup & 0x02)
return r;
issuer = zxid_extract_issuer(cf, cgi, ses, r);
if (!issuer)
return 0;
if (!cgi->sig || !*cgi->sig) {
D("Redirect or POST was not signed at binding level %d", 0);
log_msg:
if (cf->log_rely_msg) {
DD("Logging... %d", 0);
/* Path will be composed of sha1 hash of the data in p, i.e. the unbase64 data. */
sha1_safe_base64(id_buf, len, p);
id_buf[27] = 0;
id_ss.len = 27;
id_ss.s = id_buf;
logpath = zxlog_path(cf, ZX_GET_CONTENT(issuer), &id_ss, ZXLOG_RELY_DIR, ZXLOG_WIR_KIND, 1);
if (logpath) {
if (chk_dup & 0x01) {
if (zxlog_dup_check(cf, logpath, "Redirect or POST assertion (unsigned)")) {
if (cf->dup_msg_fatal) {
cgi->err = "C Duplicate message";
r = 0;
}
}
}
id_ss.len = len;
id_ss.s = p;
zxlog_blob(cf, cf->log_rely_msg, logpath, &id_ss, "dec_redir_post nosig");
}
}
return r;
}
meta = zxid_get_ent_ss(cf, ZX_GET_CONTENT(issuer));
if (!meta) {
ERR("Unable to find metadata for Issuer(%.*s) in Redir or SimpleSign POST binding", ZX_GET_CONTENT_LEN(issuer), ZX_GET_CONTENT_S(issuer));
cgi->sigval = "I";
cgi->sigmsg = "Issuer unknown - metadata exchange may be needed (SimpleSign, Redir, POST).";
ses->sigres = ZXSIG_NO_SIG;
goto log_msg;
}
/* ----- Signed at binding level ----- */
if (simplesig) {
/* In SimpleSign the signature is over data inside base64. */
p2 = p = cgi->sigalg;
URL_DECODE(p, p2, cgi->sigalg + strlen(cgi->sigalg));
*p = 0;
#if 1
/* Original SimpleSign specification was ambiguous about handling of missing
* relay state. Literal reading of the spec seemed to say that empty relay state
* should be part of the signature computation. This was reported by yours
* truly to SSTC, which has since issued errata clarifying that if the relay
* state is empty, then the RelayState label is omitted from signature
* computation. This is also consistent with how the redirect binding works. */
if (cgi->rs && *cgi->rs)
ss = zx_strf(cf->ctx, "%s=%s&RelayState=%s&SigAlg=%s&Signature=%s",
field, msg, cgi->rs, STRNULLCHK(cgi->sigalg), STRNULLCHK(cgi->sig));
else
ss = zx_strf(cf->ctx, "%s=%s&SigAlg=%s&Signature=%s",
field, msg, STRNULLCHK(cgi->sigalg), STRNULLCHK(cgi->sig));
#else
cgi->rs = "Fake";
ss = zx_strf(cf->ctx, "%s=%s&RelayState=%s&SigAlg=%s&Signature=%s",
field, msg, STRNULLCHK(cgi->rs), STRNULLCHK(cgi->sigalg), STRNULLCHK(cgi->sig));
#endif
} else {
/* In Redir binding, the signature is over base64 and url encoded data. This complicates
* life as we need to know what the URL looked like prior to CGI processing
* such as URL decoding. As such processing is done by default to all
* query string fields, this requires special processing. zxid_parse_cgi()
* has special case code to prevent URL decoding of SAMLRequest and SAMLResponse
* fields so the b64msg valiable actually has the URL encoding as well. The
* unbase64_raw() function is smart enough to unravel the URL decoding on
* the fly, so it all ends up working fine. */
if (cgi->rs && *cgi->rs)
ss = zx_strf(cf->ctx, "%s=%s&RelayState=%s&SigAlg=%s&Signature=%s",
field, b64msg, cgi->rs /* *** should be URL encoded or preserved? */,
STRNULLCHK(cgi->sigalg), STRNULLCHK(cgi->sig));
else
ss = zx_strf(cf->ctx, "%s=%s&SigAlg=%s&Signature=%s",
field, b64msg, STRNULLCHK(cgi->sigalg), STRNULLCHK(cgi->sig));
}
DD("Signed data(%.*s) len=%d sig(%s)", ss->len, ss->s, ss->len, cgi->sig);
p2 = unbase64_raw(cgi->sig, cgi->sig + strlen(cgi->sig), sigbuf, zx_std_index_64);
ASSERTOPI(p2-sigbuf, <, sizeof(sigbuf));
/* strcmp(cgi->sigalg, SIG_ALGO_RSA_SHA1) would be the right test, but as
* SigAlg can be arbitrarily URL encoded, we make the match fuzzier. */
if (cgi->sigalg && strstr(cgi->sigalg, "rsa-sha1")) {
ses->sigres = zxsig_verify_data(ss->len /* Adjust for Signature= which we log */
- (sizeof("&Signature=")-1 + strlen(cgi->sig)),
ss->s, p2-sigbuf, sigbuf,
meta->sign_cert, "Simple or Redir SigVfy");
zxid_sigres_map(ses->sigres, &cgi->sigval, &cgi->sigmsg);
} else {
ERR("Unsupported or bad signature algorithm(%s).", STRNULLCHK(cgi->sigalg));
cgi->sigval = "I";
cgi->sigmsg = "Unsupported or bad signature algorithm (in SimpleSign, Redir, or POST).";
ses->sigres = ZXSIG_NO_SIG;
}
DD("Signed data(%.*s) len=%d", ss->len, ss->s, ss->len);
if (cf->log_rely_msg) {
DD("Logging... %d", 0);
sha1_safe_base64(id_buf, ss->len, ss->s);
id_buf[27] = 0;
id_ss.len = 27;
id_ss.s = id_buf;
logpath = zxlog_path(cf, ZX_GET_CONTENT(issuer), &id_ss, ZXLOG_RELY_DIR, ZXLOG_WIR_KIND, 1);
if (logpath) {
if (zxlog_dup_check(cf, logpath, "Redirect or POST assertion")) {
if (cf->dup_msg_fatal) {
cgi->err = "C Duplicate message";
r = 0;
}
}
zxlog_blob(cf, cf->log_rely_msg, logpath, ss, "dec_redir_post sig");
}
}
zx_str_free(cf->ctx, ss);
return r;
}
/* EOF -- zxiddec.c */