71
71
int oidc_base64url_encode(request_rec *r, char **dst, const char *src,
72
72
int src_len, int remove_padding) {
73
73
if ((src == NULL) || (src_len <= 0)) {
74
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
75
"oidc_base64url_encode: not encoding anything; src=NULL and/or src_len<1");
74
oidc_error(r, "not encoding anything; src=NULL and/or src_len<1");
78
77
int enc_len = apr_base64_encode_len(src_len);
107
106
int oidc_base64url_decode(request_rec *r, char **dst, const char *src,
108
107
int add_padding) {
109
108
if (src == NULL) {
110
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
111
"oidc_base64url_decode: not decoding anything; src=NULL");
109
oidc_error(r, "not decoding anything; src=NULL");
114
112
char *dec = apr_pstrdup(r->pool, src);
152
150
unsigned char *crypted = oidc_crypto_aes_encrypt(r, c,
153
151
(unsigned char *) src, &crypted_len);
154
152
if (crypted == NULL) {
155
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
156
"oidc_encrypt_base64url_encode_string: oidc_crypto_aes_encrypt failed");
153
oidc_error(r, "oidc_crypto_aes_encrypt failed");
159
156
return oidc_base64url_encode(r, dst, (const char *) crypted, crypted_len, 1);
169
166
char *decbuf = NULL;
170
167
int dec_len = oidc_base64url_decode(r, &decbuf, src, 1);
171
168
if (dec_len <= 0) {
172
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
173
"oidc_base64url_decode_decrypt_string: oidc_base64url_decode failed");
169
oidc_error(r, "oidc_base64url_decode failed");
176
172
*dst = (char *) oidc_crypto_aes_decrypt(r, c, (unsigned char *) decbuf,
178
174
if (*dst == NULL) {
179
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
180
"oidc_base64url_decode_decrypt_string: oidc_crypto_aes_decrypt failed");
175
oidc_error(r, "oidc_crypto_aes_decrypt failed");
236
231
char *oidc_util_escape_string(const request_rec *r, const char *str) {
237
232
CURL *curl = curl_easy_init();
238
233
if (curl == NULL) {
239
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
240
"oidc_escape_string: curl_easy_init() error");
234
oidc_error(r, "curl_easy_init() error");
243
237
char *result = curl_easy_escape(curl, str, 0);
244
238
if (result == NULL) {
245
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
246
"oidc_escape_string: curl_easy_escape() error");
239
oidc_error(r, "curl_easy_escape() error");
249
242
char *rv = apr_pstrdup(r->pool, result);
258
251
char *oidc_util_unescape_string(const request_rec *r, const char *str) {
259
252
CURL *curl = curl_easy_init();
260
253
if (curl == NULL) {
261
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
262
"oidc_util_unescape_string: curl_easy_init() error");
254
oidc_error(r, "curl_easy_init() error");
265
257
char *result = curl_easy_unescape(curl, str, 0, 0);
266
258
if (result == NULL) {
267
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
268
"oidc_util_unescape_string: curl_easy_unescape() error");
259
oidc_error(r, "curl_easy_unescape() error");
271
262
char *rv = apr_pstrdup(r->pool, result);
272
263
curl_free(result);
273
264
curl_easy_cleanup(curl);
274
//ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_util_unescape_string: input=\"%s\", output=\"%s\"", str, rv);
265
//oidc_debug(r, "input=\"%s\", output=\"%s\"", str, rv);
270
* HTML escape a string
272
char *oidc_util_html_escape(apr_pool_t *pool, const char *s) {
273
const char chars[6] = { '&', '\'', '\"', '>', '<', '\0' };
274
const char * const replace[] =
275
{ "&", "'", """, ">", "<", };
276
unsigned int i, j = 0, k, n = 0, len = strlen(chars);
278
char *r = apr_pcalloc(pool, strlen(s) * 6);
279
for (i = 0; i < strlen(s); i++) {
280
for (n = 0; n < len; n++) {
281
if (s[i] == chars[n]) {
282
m = strlen(replace[n]);
283
for (k = 0; k < m; k++)
284
r[j + k] = replace[n][k];
295
return apr_pstrdup(pool, r);
279
300
* get the URL scheme that is currently being accessed
281
302
static const char *oidc_get_current_url_scheme(const request_rec *r) {
339
360
r->uri, (r->args != NULL && *r->args != '\0' ? "?" : ""), r->args,
342
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
343
"oidc_get_current_url: current URL '%s'", url);
363
oidc_debug(r, "current URL '%s'", url);
401
421
struct curl_slist *h_list = NULL;
403
423
/* do some logging about the inputs */
404
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
405
"oidc_util_http_call: url=%s, data=%s, content_type=%s, basic_auth=%s, bearer_token=%s, ssl_validate_server=%d",
425
"url=%s, data=%s, content_type=%s, basic_auth=%s, bearer_token=%s, ssl_validate_server=%d",
406
426
url, data, content_type, basic_auth, bearer_token,
407
427
ssl_validate_server);
409
429
curl = curl_easy_init();
410
430
if (curl == NULL) {
411
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
412
"oidc_util_http_call: curl_easy_init() error");
431
oidc_error(r, "curl_easy_init() error");
486
505
/* call it and record the result */
488
507
if (curl_easy_perform(curl) != CURLE_OK) {
489
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
490
"oidc_util_http_call: curl_easy_perform() failed on: %s (%s)",
508
oidc_error(r, "curl_easy_perform() failed on: %s (%s)", url, curlError);
496
513
*response = apr_pstrndup(r->pool, curlBuffer.buf, curlBuffer.written);
498
515
/* set and log the response */
499
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
500
"oidc_util_http_call: response=%s", *response);
516
oidc_debug(r, "response=%s", *response);
522
538
apr_table_do(oidc_http_add_form_url_encoded_param, &data, params, NULL);
523
539
const char *sep = strchr(url, '?') != NULL ? "&" : "?";
524
540
url = apr_psprintf(r->pool, "%s%s%s", url, sep, data.encoded_params);
525
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
526
"oidc_util_http_get: get URL=\"%s\"", url);
541
oidc_debug(r, "get URL=\"%s\"", url);
529
544
return oidc_util_http_call(r, url, NULL, NULL, basic_auth, bearer_token,
544
559
apr_table_do(oidc_http_add_form_url_encoded_param, &encode_data, params,
546
561
data = encode_data.encoded_params;
547
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
548
"oidc_util_http_post_form: post data=\"%s\"", data);
562
oidc_debug(r, "post data=\"%s\"", data);
551
565
return oidc_util_http_call(r, url, data,
599
613
if (strncmp(d->cookie_path, requestPath, strlen(d->cookie_path)) == 0)
600
614
rv = d->cookie_path;
602
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
603
"oidc_util_get_cookie_path: OIDCCookiePath (%s) not a substring of request path, using request path (%s) for cookie",
617
"OIDCCookiePath (%s) not a substring of request path, using request path (%s) for cookie",
604
618
d->cookie_path, requestPath);
605
619
rv = requestPath;
614
628
* set a cookie in the HTTP response headers
616
630
void oidc_util_set_cookie(request_rec *r, const char *cookieName,
617
const char *cookieValue) {
631
const char *cookieValue, apr_time_t expires) {
619
633
oidc_cfg *c = ap_get_module_config(r->server->module_config,
620
634
&auth_openidc_module);
621
char *headerString, *currentCookies;
635
char *headerString, *currentCookies, *expiresString = NULL;
637
/* see if we need to clear the cookie */
638
if (apr_strnatcmp(cookieValue, "") == 0)
641
/* construct the expire value */
643
expiresString = (char *) apr_pcalloc(r->pool, APR_RFC822_DATE_LEN);
644
if (apr_rfc822_date(expiresString, expires) != APR_SUCCESS) {
645
oidc_error(r, "could not set cookie expiry date");
622
649
/* construct the cookie value */
623
headerString = apr_psprintf(r->pool, "%s=%s;%s;Path=%s%s%s", cookieName,
650
headerString = apr_psprintf(r->pool, "%s=%s;%s;Path=%s%s%s%s", cookieName,
625
652
((apr_strnatcasecmp("https", oidc_get_current_url_scheme(r)) == 0) ?
626
653
";Secure" : ""), oidc_util_get_cookie_path(r),
627
654
c->cookie_domain != NULL ?
628
655
apr_psprintf(r->pool, ";Domain=%s", c->cookie_domain) : "",
629
c->cookie_http_only != FALSE ? ";HttpOnly" : "");
631
/* see if we need to clear the cookie */
632
if (apr_strnatcmp(cookieValue, "") == 0)
633
headerString = apr_psprintf(r->pool, "%s;expires=0;Max-Age=0",
656
c->cookie_http_only != FALSE ? ";HttpOnly" : "",
657
(expiresString == NULL) ?
658
"" : apr_psprintf(r->pool, "; expires=%s", expiresString));
636
660
/* use r->err_headers_out so we always print our headers (even on 302 redirect) - headers_out only prints on 2xx responses */
637
661
apr_table_add(r->err_headers_out, "Set-Cookie", headerString);
645
669
(apr_pstrcat(r->pool, headerString, ";", currentCookies, NULL)));
647
671
/* do some logging */
648
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
649
"oidc_util_set_cookie: adding outgoing header: Set-Cookie: %s",
672
oidc_debug(r, "adding outgoing header: Set-Cookie: %s", headerString);
654
676
* get a cookie from the HTTP request
656
char *oidc_util_get_cookie(request_rec *r, char *cookieName) {
678
char *oidc_util_get_cookie(request_rec *r, const char *cookieName) {
657
679
char *cookie, *tokenizerCtx, *rv = NULL;
659
681
/* get the Cookie value */
689
711
/* log what we've found */
690
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_get_cookie: returning %s",
712
oidc_debug(r, "returning %s", rv);
726
747
apr_byte_t oidc_util_request_matches_url(request_rec *r, const char *url) {
749
memset(&uri, 0, sizeof(apr_uri_t));
728
750
apr_uri_parse(r->pool, url, &uri);
730
752
(apr_strnatcmp(r->parsed_uri.path, uri.path) == 0) ? TRUE : FALSE;
731
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
732
"oidc_request_matches_url: comparing \"%s\"==\"%s\" (%d)",
733
r->parsed_uri.path, uri.path, rc);
753
oidc_debug(r, "comparing \"%s\"==\"%s\" (%d)", r->parsed_uri.path, uri.path,
784
805
json_t *value = json_object_get(result, key);
785
806
if (value != NULL) {
786
807
if (json_is_string(value)) {
787
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
788
809
"%s: response contained a \"%s\" key with string value: \"%s\"",
789
810
log, key, json_string_value(value));
791
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
792
813
"%s: response contained an \"%s\" key but no string value",
822
843
/* decode the JSON contents of the buffer */
823
844
if (*json == NULL) {
824
845
/* something went wrong */
825
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
826
"oidc_util_check_json_error: JSON parsing returned an error: %s",
846
oidc_error(r, "JSON parsing returned an error: %s", json_error.text);
831
850
if (!json_is_object(*json)) {
832
851
/* oops, no JSON */
833
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
834
"oidc_util_check_json_error: parsed JSON did not contain a JSON object");
852
oidc_error(r, "parsed JSON did not contain a JSON object");
835
853
json_decref(*json);
851
* sends HTML content to the user agent
869
* sends content to the user agent
853
int oidc_util_http_sendstring(request_rec *r, const char *html,
871
int oidc_util_http_send(request_rec *r, const char *data, int data_len, const char *content_type,
854
872
int success_rvalue) {
855
ap_set_content_type(r, "text/html");
873
ap_set_content_type(r, content_type);
856
874
apr_bucket_brigade *bb = apr_brigade_create(r->pool,
857
875
r->connection->bucket_alloc);
858
apr_bucket *b = apr_bucket_transient_create(html, strlen(html),
876
apr_bucket *b = apr_bucket_transient_create(data, data_len,
859
877
r->connection->bucket_alloc);
860
878
APR_BRIGADE_INSERT_TAIL(bb, b);
861
879
b = apr_bucket_eos_create(r->connection->bucket_alloc);
888
* sends HTML content to the user agent
890
int oidc_util_html_send(request_rec *r, const char *html,
891
int success_rvalue) {
892
return oidc_util_http_send(r, html, strlen(html), "text/html", success_rvalue);
870
896
* read all bytes from the HTTP request
872
898
static apr_byte_t oidc_util_read(request_rec *r, const char **rbuf) {
954
980
/* open the file if it exists */
955
981
if ((rc = apr_file_open(&fd, path, APR_FOPEN_READ | APR_FOPEN_BUFFERED,
956
982
APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
957
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
958
"oidc_util_file_read: no file found at: \"%s\"", path);
983
oidc_warn(r, "no file found at: \"%s\"", path);
969
994
/* get the file info so we know its size */
970
995
if ((rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, fd)) != APR_SUCCESS) {
971
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
972
"oidc_util_file_read: error calling apr_file_info_get on file: \"%s\" (%s)",
996
oidc_error(r, "error calling apr_file_info_get on file: \"%s\" (%s)",
973
997
path, apr_strerror(rc, s_err, sizeof(s_err)));
974
998
goto error_close;
981
1005
apr_size_t bytes_read = 0;
982
1006
if ((rc = apr_file_read_full(fd, *result, finfo.size, &bytes_read))
983
1007
!= APR_SUCCESS) {
984
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
985
"oidc_util_file_read: apr_file_read_full on (%s) returned an error: %s",
986
path, apr_strerror(rc, s_err, sizeof(s_err)));
1008
oidc_error(r, "apr_file_read_full on (%s) returned an error: %s", path,
1009
apr_strerror(rc, s_err, sizeof(s_err)));
987
1010
goto error_close;
993
1016
/* check that we've got all of it */
994
1017
if (bytes_read != finfo.size) {
995
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
996
"oidc_util_file_read: apr_file_read_full on (%s) returned less bytes (%" APR_SIZE_T_FMT ") than expected: (%" APR_OFF_T_FMT ")",
1019
"apr_file_read_full on (%s) returned less bytes (%" APR_SIZE_T_FMT ") than expected: (%" APR_OFF_T_FMT ")",
997
1020
path, bytes_read, finfo.size);
998
1021
goto error_close;
1003
1026
apr_file_close(fd);
1005
1028
/* log successful content retrieval */
1006
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1007
"oidc_util_file_read: file read successfully \"%s\"", path);
1029
oidc_debug(r, "file read successfully \"%s\"", path);
1046
1067
int oidc_util_html_send_error(request_rec *r, const char *error,
1047
1068
const char *description, int status_code) {
1048
char *msg = "<p>the OpenID Connect Provider returned an error:</p><p>";
1070
"<html><body><p>the OpenID Connect Provider returned an error:</p>";
1050
1072
if (error != NULL) {
1051
1073
msg = apr_psprintf(r->pool, "%s<p>Error: <pre>%s</pre></p>", msg,
1074
oidc_util_html_escape(r->pool, error));
1054
1076
if (description != NULL) {
1055
1077
msg = apr_psprintf(r->pool, "%s<p>Description: <pre>%s</pre></p>", msg,
1078
oidc_util_html_escape(r->pool, description));
1059
return oidc_util_http_sendstring(r, msg, status_code);
1081
msg = apr_psprintf(r->pool, "%s</body></html>", msg);
1083
return oidc_util_html_send(r, msg, status_code);
1072
1096
for (i = 0; i < json_array_size(haystack); i++) {
1073
1097
json_t *elem = json_array_get(haystack, i);
1074
1098
if (!json_is_string(elem)) {
1075
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1076
"oidc_util_json_array_has_value: unhandled in-array JSON non-string object type [%d]",
1099
oidc_error(r, "unhandled in-array JSON non-string object type [%d]",
1085
// ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1086
// "oidc_util_json_array_has_value: returning (%d=%d)", i,
1109
// "returning (%d=%d)", i,
1087
1110
// haystack->value.array->nelts);
1089
1112
return (i == json_array_size(haystack)) ? FALSE : TRUE;
1100
1123
oidc_normalize_header_name(r, s_key));
1102
1125
/* do some logging about this event */
1103
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1104
"oidc_util_set_app_header: setting header \"%s: %s\"", s_name,
1126
oidc_debug(r, "setting header \"%s: %s\"", s_name, s_value);
1107
1128
/* now set the actual header name/value */
1108
1129
apr_table_set(r->headers_in, s_name, s_value);
1121
1142
/* if not attributes are set, nothing needs to be done */
1122
1143
if (j_attrs == NULL) {
1123
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1124
"oidc_util_set_app_headers: no attributes to set");
1144
oidc_debug(r, "no attributes to set");
1157
1177
/* set long value in the application header whose name is based on the key and the prefix */
1158
1178
oidc_util_set_app_header(r, s_key, s_int, claim_prefix);
1160
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1161
"oidc_util_set_app_headers: could not convert JSON number to string (> 255 characters?), skipping");
1181
"could not convert JSON number to string (> 255 characters?), skipping");
1164
1184
} else if (json_is_real(j_value)) {
1179
1199
} else if (json_is_array(j_value)) {
1181
1201
/* some logging about what we're going to do */
1182
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1183
"oidc_util_set_app_headers: parsing attribute array for key \"%s\" (#nr-of-elems: %zu)",
1203
"parsing attribute array for key \"%s\" (#nr-of-elems: %zu)",
1184
1204
s_key, json_array_size(j_value));
1186
1206
/* string to hold the concatenated array string values */
1222
1242
/* don't know how to handle a non-string array element */
1223
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1224
"oidc_util_set_app_headers: unhandled in-array JSON object type [%d] for key \"%s\" when parsing claims array elements",
1244
"unhandled in-array JSON object type [%d] for key \"%s\" when parsing claims array elements",
1225
1245
elem->type, s_key);
1234
1254
/* no string and no array, so unclear how to handle this */
1235
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1236
"oidc_util_set_app_headers: unhandled JSON object type [%d] for key \"%s\" when parsing claims",
1256
"unhandled JSON object type [%d] for key \"%s\" when parsing claims",
1237
1257
j_value->type, s_key);