Branch data Line data Source code
1 : : /* zxidcgi.c - Handwritten functions for parsing SP specific CGI options
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: zxidcgi.c,v 1.33 2010-01-08 02:10:09 sampo Exp $
10 : : *
11 : : * 12.8.2006, created --Sampo
12 : : * 16.1.2007, split from zxidlib.c --Sampo
13 : : * 12.10.2007, added cookie scanning --Sampo
14 : : * 7.10.2008, added documentation --Sampo
15 : : *
16 : : * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
17 : : */
18 : :
19 : : #include <memory.h>
20 : : #include <string.h>
21 : : #include "errmac.h"
22 : : #include "zxid.h"
23 : : #include "zxidconf.h"
24 : :
25 : : /* ============== CGI Parsing ============== */
26 : :
27 : : /*(i) Parse query string or form POST and detect parameters relevant for ZXID.
28 : : * N.B. This CGI parsing is very specific for needs of ZXID. It is not generic.
29 : : *
30 : : * cgi:: Already allocated CGI structure where results of this function
31 : : * are deposited. Note that this structure is not cleared. Thus it is
32 : : * possible to call zxid_parse_cgi() multiple times to accumulate
33 : : * results from multiple sources, e.g. foirst for query string, and then
34 : : * for form POST.
35 : : * qs:: CGI formatted input. Usually query string or form POST content.
36 : : * return:: 0 on success. Other values reserved. Usually return value is
37 : : * ignored as there really is no way for this function to fail. Unrecognized
38 : : * CGI arguments are simply ignored with assumption that some other processing
39 : : * layer will pick them up - hence no need to flag error. */
40 : :
41 : : /* Called by: chkuid x3, main x4, zxid_decode_ssoreq, zxid_new_cgi, zxid_simple_cf_ses x3 */
42 : : int zxid_parse_cgi(zxid_cgi* cgi, char* qs)
43 : 203 : {
44 : : char *p, *n, *v, *val, *name;
45 : : DD("qs(%s) len=%d", STRNULLCHK(qs), qs?strlen(qs):-1);
46 [ - + ]: 203 : if (!qs)
47 : 0 : return 0;
48 [ + - + + ]: 752 : while (qs && *qs) {
49 [ + + ]: 346 : for (; *qs == '&'; ++qs) ; /* Skip over & or && */
50 [ + - ]: 346 : if (!*qs) break;
51 : :
52 : 346 : qs = strchr(name = qs, '='); /* Scan name (until '=') */
53 [ + - ]: 346 : if (!qs) break;
54 [ + + ]: 346 : if (qs == name) { /* Key was an empty string: skip it */
55 : 1 : qs = strchr(qs, '&'); /* Scan value (until '&') *** or '?' */
56 : 1 : continue;
57 : : }
58 [ + - - + ]: 345 : for (; name < qs && *name <= ' '; ++name) ; /* Skip over initial whitespace before name */
59 : 345 : n = p = name;
60 [ + + + - : 345 : URL_DECODE(p, name, qs);
+ - - + #
# # # # #
# # + - +
- + - - +
# # # # +
- + - + -
- + - + +
+ ]
61 : 345 : *p = 0; /* Nul-term n (name) */
62 : :
63 [ + + + + ]: 345 : for (val = ++qs; *qs && *qs != '&'; ++qs) ; /* Skip over = and scan value (until '&') */
64 : 345 : v = p = val;
65 : : /* SAMLRequest and Response MUST NOT be URL decoded as the URL encoding
66 : : * is needed for redirect binding signature validation. See also unbase64_raw()
67 : : * for how these fields are URL decoded at later stage. */
68 [ + + + + : 648 : if (n[0] != 'S' && n[0] != 'R'
+ + + + +
+ + + +
+ ]
69 : : || strcmp(n, "SAMLRequest") && strcmp(n, "SAMLResponse")
70 : : && strcmp(n, "SigAlg") && strcmp(n, "Signature") && strcmp(n, "RelayState"))
71 [ + + + - : 303 : URL_DECODE(p, val, qs);
+ - - + #
# # # # #
# # + - +
+ + - - +
# # # # +
- + - + -
+ + + + +
+ ]
72 : : else
73 : 42 : p = qs;
74 : :
75 [ + + ]: 345 : if (*qs)
76 : 142 : ++qs;
77 : 345 : *p = 0; /* Nul-term v (value) */
78 : :
79 [ + + + + : 345 : switch (n[0]) {
+ + + + +
+ + + +
- ]
80 : : case 'o':
81 [ + + ]: 180 : if (!n[1]) { cgi->op = v[0]; break; }
82 [ + + ]: 2 : if (n[1] = 'k' && !n[2]) { cgi->ok = v; break; } /* ok button */
83 : 1 : goto unknown;
84 : : case 's':
85 [ + - ]: 8 : if (!n[1]) { cgi->sid = v; break; }
86 : 0 : goto unknown;
87 : : case 'c':
88 [ + - ]: 1 : if (!n[1]) { cgi->cdc = v; break; }
89 : 0 : goto unknown;
90 : : /* The following two entity IDs, combined with various login buttons
91 : : * aim at supporting may different user interface layouts. You need to
92 : : * understand how they interact to avoid undesired conflicts. */
93 : : case 'e': /* EntityID field (manual entry). Overrides 'd'. */
94 [ + - ]: 6 : if (!n[1]) {
95 : 6 : set_eid:
96 [ + + ]: 6 : if (v[0]) cgi->eid = v;
97 : : DD("v(%s) v0=0x%02x v=%p cgi->eid=%p cgi=%p", v, v[0], v, cgi->eid, cgi);
98 : 6 : break;
99 : : }
100 : 0 : goto unknown;
101 : : case 'd': /* EntityID popup or radio box */
102 [ + - ]: 1 : if (!n[1]) {
103 [ + - + - ]: 1 : if (cgi->eid && cgi->eid[0]) {
104 [ - + # # ]: 1 : D("EID already set v(%s) v0=0x%02x v=%p cgi->eid=%p cgi=%p", v, v[0], v, cgi->eid, cgi);
105 : 1 : break;
106 : : }
107 : 0 : goto set_eid;
108 : : }
109 : 0 : goto unknown;
110 : : case 'l':
111 : : /* Login button names are like lP<eid>, where "l" is literal ell, P is
112 : : * protocol profile designator, and <eid> is Entity ID of the IdP.
113 : : * N.B. If eid is omitted from button name, it may be provided using
114 : : * d or e fields (see above). */
115 : 8 : cgi->pr_ix = n[1];
116 [ + + ]: 8 : if (n[2])
117 : 7 : cgi->eid = n+2;
118 : 8 : cgi->op = 'L';
119 [ + + - + ]: 8 : D("cgi: login eid(%s)", cgi->eid);
120 : 8 : break;
121 : : case 'i':
122 [ + - ]: 1 : if (!strcmp(n, "inv")) {
123 : 1 : cgi->inv = v;
124 : 1 : break;
125 : : }
126 : : /* IdP and protocol index selection popup values are like P<eid>
127 : : * N.B. If eid is omitted from button name, it may be provided using
128 : : * d or e fields (see above). This effectively allows i to be just
129 : : * a protocol selection popup. */
130 : 0 : cgi->pr_ix = v[0];
131 [ # # ]: 0 : if (v[1])
132 : 0 : cgi->eid = v+1;
133 : 0 : break;
134 : : case 'f': /* flags and (hidden) fields found in typical SP login form */
135 [ + - + - ]: 67 : if (!n[1] || n[2]) goto unknown;
136 [ + + + + : 67 : switch (n[1]) {
+ + + + +
+ + - ]
137 [ + - - + ]: 7 : case 'a': cgi->authn_ctx = v; D("authn_ctx=%s", cgi->authn_ctx); break;
138 [ + - - + ]: 7 : case 'c': cgi->allow_create = v[0]; D("allow_create=%c", cgi->allow_create); break;
139 [ + - - + ]: 7 : case 'f': cgi->force_authn = v[0]; D("force_authn=%c", cgi->force_authn); break;
140 : 1 : case 'g': cgi->get_complete = v; break;
141 : 1 : case 'h': cgi->pxy_count = v; break;
142 : : /*case 'i': cgi->idp_list = v; break;*/
143 : 7 : case 'm': cgi->matching_rule = v; break;
144 [ + - - + ]: 7 : case 'n': cgi->nid_fmt = v; D("nid_fmt=%s", cgi->nid_fmt); break;
145 [ + - - + ]: 7 : case 'p': cgi->ispassive = v[0]; D("ispassive=%c", cgi->ispassive); break;
146 : 7 : case 'q': cgi->affil = v; break;
147 : 9 : case 'r': cgi->rs = v; break;
148 : 7 : case 'y': cgi->consent = v; break;
149 : : }
150 : 67 : break;
151 : : case 'g':
152 [ + - + - ]: 15 : if (!n[1] || n[2]) goto unknown;
153 [ + + + + ]: 15 : switch (n[1]) {
154 : : case 'l':
155 : : case 'r':
156 : : case 's':
157 : : case 't':
158 : 12 : case 'u': cgi->op = n[1]; break;
159 : 1 : case 'n': cgi->newnym = v; break;
160 : 1 : case 'e': cgi->enc_hint = v[0]; break;
161 : : }
162 : 15 : break;
163 : : case 'a':
164 [ + - ]: 13 : if (!n[1]) goto unknown;
165 [ + + + + : 13 : switch (n[1]) {
+ + + - ]
166 [ + - ]: 2 : case 'l': if (n[3]) goto unknown; cgi->op = n[2]; break;
167 [ + - + + : 4 : case 'u': if (n[2]) goto unknown; if (v[0] || !cgi->uid) cgi->uid = v; break;
+ - ]
168 [ + - ]: 2 : case 'p': if (n[2]) goto unknown; cgi->pw = v; break;
169 [ + - ]: 2 : case 'r': if (n[2]) goto unknown; cgi->ssoreq = v; break;
170 [ + - ]: 1 : case 'n': if (n[2]) goto unknown; cgi->op = 'N'; break;
171 [ + - ]: 1 : case 'w': if (n[2]) goto unknown; cgi->op = 'W'; break;
172 [ + - ]: 1 : case 't': if (n[2]) goto unknown; cgi->atselafter = 1; break;
173 : : }
174 : 13 : break;
175 : : case 'z':
176 [ + - ]: 2 : switch (n[1]) {
177 : : case 'x':
178 [ + - - ]: 2 : switch (n[2]) {
179 : 2 : case 'a': cgi->zxapp = v; break;
180 : 0 : case 'r': cgi->zxrfr = v; break;
181 : : }
182 : : break;
183 : : }
184 : 2 : break;
185 : : case 'R':
186 [ + - ]: 6 : if (!strcmp(n, "RelayState")) {
187 : 6 : cgi->rs = v;
188 : 6 : break;
189 : : }
190 : 0 : break;
191 : : case 'S':
192 [ + + ]: 37 : if (!strcmp(n, "SAMLart")) {
193 : 1 : cgi->saml_art = v;
194 : 1 : cgi->op = 'A';
195 : 1 : break;
196 : : }
197 [ + + ]: 36 : if (!strcmp(n, "SAMLResponse")) {
198 : 8 : cgi->saml_resp = v;
199 : 8 : cgi->op = 'P';
200 : 8 : break;
201 : : }
202 [ + + ]: 28 : if (!strcmp(n, "SAMLRequest")) {
203 : 8 : cgi->saml_req = v;
204 [ + - + + : 8 : if (!ONE_OF_3(cgi->op, 'p', 'F', 'R')) /* Avoid redundant sigvfy and processing for IdP */
+ - ]
205 : 2 : cgi->op = 'Q';
206 : 8 : break;
207 : : }
208 [ + + ]: 20 : if (!strcmp(n, "SigAlg")) {
209 : 10 : cgi->sigalg = v;
210 : 10 : break;
211 : : }
212 [ + - ]: 10 : if (!strcmp(n, "Signature")) {
213 : 10 : cgi->sig = v;
214 : 10 : break;
215 : : }
216 : : /* fall thru */
217 : 1 : unknown:
218 [ - + # # ]: 1 : default: D("Unknown CGI field(%s) val(%s)", n, v);
219 : : }
220 : : }
221 : : DD("END cgi=%p cgi->eid=%p (%s) op(%c) magic=%x", cgi, cgi->eid, cgi->eid, cgi->op, cgi->magic);
222 : 203 : return 0;
223 : : }
224 : :
225 : : /* Called by: covimp_test */
226 : : zxid_cgi* zxid_new_cgi(zxid_conf* cf, char* qs)
227 : 1 : {
228 : 1 : zxid_cgi* cgi = ZX_ZALLOC(cf->ctx, zxid_cgi);
229 : 1 : cgi->magic = ZXID_CGI_MAGIC;
230 [ + - ]: 1 : if (qs) {
231 : : char* qqs;
232 : 1 : int len = strlen(qs);
233 : 1 : qqs = ZX_ALLOC(cf->ctx, len+1);
234 : 1 : memcpy(qqs, qs, len);
235 : 1 : qqs[len] = 0;
236 : 1 : zxid_parse_cgi(cgi, qqs);
237 : : }
238 : 1 : return cgi;
239 : : }
240 : :
241 : : /*() Try to extract session ID from a cookie. The extracted value, if any,
242 : : * will be deposited in cgi->sid. If no session ID is found, then cgi->sid
243 : : * is not modified. The name of the cookie is determined by configuration
244 : : * option ~SES_COOKIE_NAME~ (see zxidconf.h).
245 : : *
246 : : * return:: none, but cgi->sid is modified
247 : : *
248 : : * For original Netscape cookie spec see: http://curl.haxx.se/rfc/cookie_spec.html (Oct2007)
249 : : *
250 : : * *Example*
251 : : *
252 : : * ONE_COOKIE=aaa; ZXIDSES=S12cvd324; SOME_OTHER_COOKIE=...
253 : : */
254 : :
255 : : /* Called by: chkuid, zxid_simple_cf_ses */
256 : : void zxid_get_sid_from_cookie(zxid_conf* cf, zxid_cgi* cgi, const char* cookie)
257 : 52 : {
258 : : char* q;
259 : : int len;
260 [ + + ]: 52 : if (!cookie)
261 : 38 : return;
262 : 14 : len = strlen(cf->ses_cookie_name);
263 : 14 : for (cookie = strstr(cookie, cf->ses_cookie_name);
264 [ + - ]: 28 : cookie;
265 : 0 : cookie = strstr(cookie + 1, cf->ses_cookie_name))
266 [ + - ]: 14 : if (cookie[len] == '=') {
267 : 14 : cookie += len + 1;
268 [ - + ]: 14 : if (*cookie == '"')
269 : 0 : ++cookie;
270 : 14 : q = strchr(cookie, ';');
271 [ - + ]: 14 : len = q ? (q-cookie) : strlen(cookie);
272 [ - + ]: 14 : if (cookie[len-1] == '"')
273 : 0 : --len;
274 : 14 : cgi->sid = ZX_ALLOC(cf->ctx, len + 1);
275 : 14 : memcpy(cgi->sid, cookie, len);
276 : 14 : cgi->sid[len] = 0;
277 : 14 : return;
278 : : }
279 : : }
280 : :
281 : : /* EOF -- zxidcgi.c */
|