LCOV - code coverage report
Current view: top level - zxid - zxidlib.c (source / functions) Hit Total Coverage
Test: ZXID Code Coverage Lines: 288 442 65.2 %
Date: 2010-12-19 Functions: 23 23 100.0 %
Branches: 160 431 37.1 %

           Branch data     Line data    Source code
       1                 :            : /* zxidlib.c  -  Handwritten functions for implementing common application 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: zxidlib.c,v 1.73 2010-01-08 02:10:09 sampo Exp $
      10                 :            :  *
      11                 :            :  * 12.8.2006, created --Sampo
      12                 :            :  * 16.1.2007, factored out ses, conf, cgi, ecp, cdc, and loc --Sampo
      13                 :            :  * 7.10.2008, added documentation --Sampo
      14                 :            :  * 4.9.2009,  added zxid_map_val() --Sampo
      15                 :            :  */
      16                 :            : 
      17                 :            : #include "platform.h"
      18                 :            : #include <string.h>
      19                 :            : #include <stdio.h>
      20                 :            : #include <sys/stat.h>  /* umask(2) */
      21                 :            : 
      22                 :            : #include "errmac.h"
      23                 :            : #include "zxid.h"
      24                 :            : #include "zxidpriv.h"
      25                 :            : #include "zxidutil.h"
      26                 :            : #include "zxidconf.h"
      27                 :            : #include "saml2.h"
      28                 :            : #include "c/zxidvers.h"
      29                 :            : #include "c/zx-const.h"
      30                 :            : #include "c/zx-ns.h"
      31                 :            : #include "c/zx-data.h"
      32                 :            : 
      33                 :            : int zx_debug = 0;              /* declared in errmac.h */
      34                 :            : char zx_indent[256] = "";      /* declared in errmac.h */
      35                 :            : char zx_instance[64] = "\tzx"; /* declared in errmac.h */
      36                 :            : int assert_nonfatal = 0;
      37                 :            : char* assert_msg = "%s: Internal error caused an ASSERT to fire. Deliberately trying to dump core.\nSorry for the inconvenience. If no core appears, try `ulimit -c unlimited'\n";
      38                 :            : int trace = 0;
      39                 :            : 
      40                 :            : /*() Obtain the hex encoded version integer describing the libzxid. This can be
      41                 :            :  * used to effectuate a runtime version number check. For compile time you
      42                 :            :  * should check the value of the ~ZXID_VERSION~ macro. */
      43                 :            : 
      44                 :            : /* Called by:  covimp_test, opt x2 */
      45                 :            : int zxid_version()
      46                 :          1 : {
      47                 :          1 :   return ZXID_VERSION;
      48                 :            : }
      49                 :            : 
      50                 :            : /*() Obtain the version string describing the libzxid. This can be
      51                 :            :  * used for runtime version display. For compile time you
      52                 :            :  * should check the value of the ~ZXID_VERSION~ macro. */
      53                 :            : 
      54                 :            : /* Called by:  main x7, opt x2, zxid_fed_mgmt_cf, zxid_idp_select_zxstr_cf_cgi, zxid_map_bangbang, zxid_mgmt */
      55                 :            : char* zxid_version_str()
      56                 :         39 : {
      57                 :         39 :   return ZXID_REL " " ZXID_COMPILE_DATE " libzxid (zxid.org)";
      58                 :            : }
      59                 :            : 
      60                 :            : /*(i) Render any element with some options, controlled by
      61                 :            :  * config option ENC_TAIL_OPT. Often used to generate slightly optimized
      62                 :            :  * version for wire transfer. Not suitable for generating canonicalization. */
      63                 :            : 
      64                 :            : /* Called by:  main x3, so_enc_dec, zxid_addmd, zxid_anoint_sso_resp, zxid_cache_epr, zxid_call_epr, zxid_idp_sso, zxid_lecp_check, zxid_map_val_ss, zxid_mk_enc_a7n, zxid_mk_enc_id, zxid_mk_mni, zxid_mni_do_ss, zxid_pep_az_base_soap_pepmap x3, zxid_pep_az_soap_pepmap x3, zxid_reg_svc, zxid_ses_to_pool x2, zxid_slo_resp_redir, zxid_snarf_eprs_from_ses, zxid_soap_call_raw, zxid_soap_cgi_resp_body, zxid_sp_meta, zxid_sp_mni_redir, zxid_sp_slo_redir, zxid_start_sso_url, zxid_write_ent_to_cache, zxid_wsc_prepare_call, zxid_wsp_decorate */
      65                 :            : struct zx_str* zx_easy_enc_elem_opt(zxid_conf* cf, struct zx_elem_s* x)
      66                 :       2298 : {
      67                 :            :   struct zx_str* ss;
      68                 :       2298 :   cf->ctx->enc_tail_opt = cf->enc_tail_opt;
      69                 :       2298 :   ss = zx_EASY_ENC_elem(cf->ctx, x);
      70                 :       2298 :   cf->ctx->enc_tail_opt = 0;
      71                 :       2298 :   return ss;
      72                 :            : }
      73                 :            : 
      74                 :            : /* Called by:  attribute_sort_test, zxid_a7n2str, zxid_anoint_a7n x2, zxid_anoint_sso_resp, zxid_az_soap x2, zxid_idp_sso, zxid_mk_art_deref, zxid_nid2str, zxid_sp_mni_soap, zxid_sp_slo_soap, zxid_sp_soap_dispatch x5, zxid_sp_sso_finalize, zxid_ssos_anreq, zxid_token2str, zxid_wsf_validate_a7n */
      75                 :            : struct zx_str* zx_easy_enc_elem_sig(zxid_conf* cf, struct zx_elem_s* x)
      76                 :       3163 : {
      77                 :       3163 :   cf->ctx->enc_tail_opt = 0;
      78                 :       3163 :   return zx_EASY_ENC_elem(cf->ctx, x);
      79                 :            : }
      80                 :            : 
      81                 :            : /*() Generate pseudorandom or statistically unique identifier of given length. The
      82                 :            :  * unique identifier will be safe base64 encoded.
      83                 :            :  *
      84                 :            :  * cf::     Configuration object, used for memory allocation.
      85                 :            :  * prefix:: A prefix string, usually used to distinguish classes of unique ids.
      86                 :            :  * bits::   Number of pseudorandom bits in the unique ID. For best results,
      87                 :            :  *     bits should be multiple of 24 (3 bytes expands to 4 safe base64 chars)
      88                 :            :  * return:: The identifier as zx_str. Caller should eventually free this memory.
      89                 :            :  */
      90                 :            : /* Called by:  zxid_check_fed, zxid_mk_subj, zxid_mk_transient_nid, zxid_ps_addent_invite x3, zxid_ps_resolv_id, zxid_put_ses, zxid_pw_authn, zxid_ssos_anreq, zxid_wsc_prep_secmech, zxid_wsf_decor */
      91                 :            : struct zx_str* zxid_mk_id(zxid_conf* cf, char* prefix, int bits)
      92                 :        268 : {
      93                 :            :   char bit_buf[ZXID_ID_MAX_BITS/8];
      94                 :            :   char base64_buf[ZXID_ID_MAX_BITS/6 + 1];
      95                 :            :   char* p;
      96   [ +  -  -  + ]:        268 :   if (bits > ZXID_ID_MAX_BITS || bits & 0x07) {
      97                 :          0 :     ERR("Requested bits(%d) more than internal limit(%d), or bits not divisible by 8.", bits, ZXID_ID_MAX_BITS);
      98                 :          0 :     return 0;
      99                 :            :   }
     100                 :        268 :   zx_rand(bit_buf, bits >> 3);
     101                 :        268 :   p = base64_fancy_raw(bit_buf, bits >> 3, base64_buf, safe_basis_64, 1<<31, 0, 0, '.');
     102         [ +  - ]:        268 :   return zx_strf(cf->ctx, "%s%.*s", prefix?prefix:"", p-base64_buf, base64_buf);
     103                 :            : }
     104                 :            : 
     105                 :            : /* Called by:  zxid_mk_a7n, zxid_mk_art_deref, zxid_mk_authn_req, zxid_mk_az, zxid_mk_az_cd1, zxid_mk_dap_query_item, zxid_mk_dap_resquery, zxid_mk_dap_subscription, zxid_mk_dap_test_item, zxid_mk_logout, zxid_mk_logout_resp, zxid_mk_mni, zxid_mk_mni_resp, zxid_mk_saml_resp */
     106                 :            : struct zx_attr_s* zxid_mk_id_attr(zxid_conf* cf, struct zx_elem_s* father, int tok, char* prefix, int bits)
     107                 :       1134 : {
     108                 :            :   char bit_buf[ZXID_ID_MAX_BITS/8];
     109                 :            :   char base64_buf[ZXID_ID_MAX_BITS/6 + 1];
     110                 :            :   char* p;
     111   [ +  -  -  + ]:       1134 :   if (bits > ZXID_ID_MAX_BITS || bits & 0x07) {
     112                 :          0 :     ERR("Requested bits(%d) more than internal limit(%d), or bits not divisible by 8.", bits, ZXID_ID_MAX_BITS);
     113                 :          0 :     return 0;
     114                 :            :   }
     115                 :       1134 :   zx_rand(bit_buf, bits >> 3);
     116                 :       1134 :   p = base64_fancy_raw(bit_buf, bits >> 3, base64_buf, safe_basis_64, 1<<31, 0, 0, '.');
     117         [ +  - ]:       1134 :   return zx_attrf(cf->ctx, father, tok, "%s%.*s", prefix?prefix:"", p-base64_buf, base64_buf);
     118                 :            : }
     119                 :            : 
     120                 :            : /*() Format a date-time string as usually used in XML, SAML, and Liberty. Apparently
     121                 :            :  * there are two ways to format this: with or with-out milliseconds. ZXID accepts
     122                 :            :  * either form as input, as they are both legal, but will only generate the
     123                 :            :  * without milliseconds form. Some other softwares are buggy and fail to
     124                 :            :  * accept the without milliseconds form. You can change the format at compile time
     125                 :            :  * by editing zxidlib.c:94.
     126                 :            :  */
     127                 :            : /* Called by:  zxid_put_invite x2, zxid_put_psobj x2, zxid_wsc_prep_secmech, zxid_wsf_decor */
     128                 :            : struct zx_str* zxid_date_time(zxid_conf* cf, time_t secs)
     129                 :        191 : {
     130                 :            :   struct tm t;
     131                 :        191 :   secs += cf->timeskew;
     132                 :        191 :   GMTIME_R(secs, t);
     133                 :            : #if 0
     134                 :            :   /*                      "2002-10-31T21:42:14.002Z" */
     135                 :            :   return zx_strf(cf->ctx, "%04d-%02d-%02dT%02d:%02d:%02d.002Z",
     136                 :            :                  t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
     137                 :            : #else
     138                 :            :   /*                      "2002-10-31T21:42:14Z" */
     139                 :        191 :   return zx_strf(cf->ctx, "%04d-%02d-%02dT%02d:%02d:%02dZ",
     140                 :            :                  t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
     141                 :            : #endif
     142                 :            : }
     143                 :            : 
     144                 :            : /* Called by:  zxid_mk_a7n x3, zxid_mk_an_stmt, zxid_mk_art_deref, zxid_mk_authn_req, zxid_mk_az, zxid_mk_az_cd1, zxid_mk_logout, zxid_mk_logout_resp, zxid_mk_mni, zxid_mk_mni_resp, zxid_mk_saml_resp, zxid_ps_addent_invite x2 */
     145                 :            : struct zx_attr_s* zxid_date_time_attr(zxid_conf* cf, struct zx_elem_s* father, int tok, time_t secs)
     146                 :       3099 : {
     147                 :            :   struct tm t;
     148                 :       3099 :   secs += cf->timeskew;
     149                 :       3099 :   GMTIME_R(secs, t);
     150                 :            : #if 0
     151                 :            :   /*                                    "2002-10-31T21:42:14.002Z" */
     152                 :            :   return zx_attrf(cf->ctx, father, tok, "%04d-%02d-%02dT%02d:%02d:%02d.002Z",
     153                 :            :                   t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
     154                 :            : #else
     155                 :            :   /*                                    "2002-10-31T21:42:14Z" */
     156                 :       3099 :   return zx_attrf(cf->ctx, father, tok, "%04d-%02d-%02dT%02d:%02d:%02dZ",
     157                 :            :                   t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
     158                 :            : #endif
     159                 :            : }
     160                 :            : 
     161                 :            : /* ============== Redirect Encodings ============= */
     162                 :            : 
     163                 :            : #define SIG_ALGO_RSA_SHA1 "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
     164                 :            : #define SIG_ALGO_RSA_SHA1_URLENC "http://www.w3.org/2000/09/xmldsig%23rsa-sha1"
     165                 :            : #define SIG_ALGO SIG_ALGO_RSA_SHA1
     166                 :            : #define SIG_ALGO_URLENC SIG_ALGO_RSA_SHA1_URLENC
     167                 :            : #define ETSIGNATURE_EQ "&Signature="
     168                 :            : #define SIG_SIZE 256  /* Maximum size of the base64 encoded signature, for buffer allocation */
     169                 :            : 
     170                 :            : /*(i) Encode (and sign if Simple Sign) a form according to SAML2 POST binding.
     171                 :            :  * zxid_decode_redir_or_post() performs the opposite operation.
     172                 :            :  *
     173                 :            :  * cf::          ZXID configuration object, also used for memory allocation
     174                 :            :  * field::       The name of the CGI variable, e.g. "SAMLRequest" or "SAMLResponse"
     175                 :            :  * payload::     What should be encoded in the redirect URL. Effectively becomes the query string
     176                 :            :  * relay_state:: Optional relay state argument. Ends up being encoded in the query string
     177                 :            :  * sign::        Whether binding layer signature is to be applied: 0=no, 1=POST-Simple-Sign
     178                 :            :  * action_url::  URL where the form should be posted
     179                 :            :  * return::      Query string encoding of the request. The memory should be freed by the caller.
     180                 :            :  *     0 on failure.  */
     181                 :            : 
     182                 :            : /* Called by:  zxid_idp_sso x3 */
     183                 :            : struct zx_str* zxid_saml2_post_enc(zxid_conf* cf, char* field, struct zx_str* payload, char* relay_state, int sign, struct zx_str* action_url)
     184                 :          5 : {
     185                 :            :   EVP_PKEY* sign_pkey;
     186                 :            :   struct zx_str id_str;
     187                 :            :   struct zx_str* logpath;
     188                 :            :   char* sigbuf[SIG_SIZE];
     189                 :            :   char* zbuf;
     190                 :            :   char* url;
     191                 :            :   char* sig;
     192                 :            :   char* p;
     193                 :            :   int alloc_len, zlen, slen, field_len, rs_len;
     194                 :            :   zxid_cgi cgi;
     195                 :          5 :   field_len = strlen(field);
     196         [ +  + ]:          5 :   rs_len = relay_state?strlen(relay_state):0;
     197         [ +  + ]:          5 :   if (rs_len) {
     198                 :            :     /* Inplace decode. Decode is needed because the browser will encode again when
     199                 :            :      * submitting the form hidden field. */
     200                 :          3 :     p = url = relay_state;
     201   [ -  +  #  #  :          3 :     URL_DECODE(p, url, relay_state + rs_len);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  -  +  +  
                      + ]
     202                 :          3 :     *p = 0;
     203                 :          3 :     rs_len = p - relay_state;
     204         [ -  + ]:          6 :     while (p = strchr(relay_state,'"')) {
     205                 :          0 :       ERR("RelayState(%s) MUST NOT contain double quote character because it would interfere with HTML form hidden field double quotes. Bad character squashed at position %d.", relay_state, p-relay_state);
     206                 :          0 :       *p = '_';
     207                 :            :     }
     208                 :            :   }
     209                 :            : 
     210                 :            :   /* The url buf is allocated large enough to be used for both signing and/or base64 encoding. */
     211                 :          5 :   alloc_len = MAX((field_len + 1 + payload->len
     212                 :            :                    + sizeof("&RelayState=")-1 + rs_len
     213                 :            :                    + sizeof("&SigAlg=" SIG_ALGO)-1 + sizeof(ETSIGNATURE_EQ)-1 + SIG_SIZE),
     214                 :            :                   SIMPLE_BASE64_LEN(payload->len));
     215                 :          5 :   url = p = ZX_ALLOC(cf->ctx, alloc_len + 1);  /* +1 for nul term */
     216                 :            : 
     217         [ -  + ]:          5 :   if (sign) {   /* Additional POST-Simple-Sign signing (sign payload prior to base64 & URL enc) */
     218                 :          0 :     memcpy(p, field, field_len);
     219                 :          0 :     p += field_len;
     220                 :          0 :     *p++ = '=';
     221                 :          0 :     memcpy(p, payload->s, payload->len);
     222                 :          0 :     p += payload->len;
     223                 :            : 
     224         [ #  # ]:          0 :     if (rs_len) {
     225                 :          0 :       memcpy(p, "&RelayState=", sizeof("&RelayState=")-1);
     226                 :          0 :       p += sizeof("&RelayState=")-1;
     227                 :          0 :       memcpy(p, relay_state, rs_len);
     228                 :          0 :       p += rs_len;
     229                 :            :     }
     230                 :            :     
     231                 :          0 :     memcpy(p, "&SigAlg=" SIG_ALGO, sizeof("&SigAlg=" SIG_ALGO)-1);
     232                 :          0 :     p += sizeof("&SigAlg=" SIG_ALGO)-1;
     233                 :            : 
     234         [ #  # ]:          0 :     if (zxid_lazy_load_sign_cert_and_pkey(cf, 0, &sign_pkey, "SAML2 post"))
     235                 :          0 :       zlen = zxsig_data(cf->ctx, p-url, url, &zbuf, sign_pkey, "SAML2 post");
     236         [ #  # ]:          0 :     if (zlen == -1)
     237                 :          0 :       return 0;
     238                 :            : 
     239                 :          0 :     memcpy(p, ETSIGNATURE_EQ, sizeof(ETSIGNATURE_EQ)-1);
     240                 :          0 :     p += sizeof(ETSIGNATURE_EQ)-1;
     241                 :          0 :     sig = p;
     242                 :          0 :     p = base64_fancy_raw(zbuf, zlen, p, std_basis_64, 1<<31, 0, 0, '=');
     243   [ #  #  #  # ]:          0 :     ASSERTOP(p-url, <, alloc_len);  /* Check sig did not overrun its fixed size alloc SIG_SIZE */
     244                 :          0 :     slen = p-sig;
     245                 :          0 :     ZX_FREE(cf->ctx, zbuf);
     246                 :            :     
     247         [ #  # ]:          0 :     if (cf->log_issue_msg) {
     248                 :          0 :       id_str.len = p-url;
     249                 :          0 :       id_str.s = url;
     250                 :          0 :       logpath = zxlog_path(cf, action_url, &id_str, ZXLOG_ISSUE_DIR, ZXLOG_WIR_KIND, 1);
     251         [ #  # ]:          0 :       if (logpath) {
     252         [ #  # ]:          0 :         if (zxlog_dup_check(cf, logpath, "IdP POST SimpleSign")) {
     253                 :          0 :           ERR("Duplicate wire msg(%.*s) (Simple Sign)", p-url, url);
     254         [ #  # ]:          0 :           if (cf->dup_msg_fatal) {
     255                 :          0 :             ERR("FATAL (by configuration): Duplicate wire msg(%.*s) (Simple Sign)", p-url, url);
     256                 :          0 :             zxlog_blob(cf, 1, logpath, &id_str, "POST SimpleSign dup");
     257                 :          0 :             zx_str_free(cf->ctx, logpath);
     258                 :          0 :             ZX_FREE(cf->ctx, url);
     259                 :          0 :             return 0;
     260                 :            :           }
     261                 :            :         }
     262                 :          0 :         zxlog_blob(cf, 1, logpath, &id_str, "POST SimpleSign");
     263                 :          0 :         zx_str_free(cf->ctx, logpath);
     264                 :            :       }
     265                 :            :     }
     266   [ #  #  #  # ]:          0 :     ASSERTOP(slen, <, SIG_SIZE-1);
     267                 :          0 :     memcpy(sigbuf, sig, slen);
     268                 :          0 :     sigbuf[slen] = 0;
     269                 :            :   } else {
     270                 :          5 :     sigbuf[0] = 0;
     271                 :            :   }
     272                 :            : 
     273                 :          5 :   p = base64_fancy_raw(payload->s, payload->len, url, std_basis_64, 1<<31, 0, 0, '=');
     274                 :          5 :   *p = 0;
     275   [ -  +  #  # ]:          5 :   ASSERTOP(p-url, <=, alloc_len);  /* Check sig did not overrun its fixed size alloc SIG_SIZE */  
     276                 :            : 
     277                 :            : #if 1
     278                 :            :   /* Template based POST page, see post.html */
     279                 :          5 :   ZERO(&cgi, sizeof(cgi));
     280                 :          5 :   cgi.action_url = zx_str_to_c(cf->ctx, action_url);
     281                 :          5 :   cgi.saml_art  = field;
     282                 :          5 :   cgi.saml_resp = url;
     283         [ +  + ]:          5 :   if (rs_len) {
     284                 :          3 :     logpath = zx_strf(cf->ctx, "<input type=hidden name=RelayState value=\"%s\">", relay_state);
     285                 :          3 :     cgi.rs = logpath->s;
     286                 :          3 :     ZX_FREE(cf->ctx, logpath);
     287                 :            :   }
     288         [ -  + ]:          5 :   if (sign) {
     289                 :          0 :     logpath = zx_strf(cf->ctx, "<input type=hidden name=SigAlg value=\"" SIG_ALGO "\"><input type=hidden name=Signature value=\"%s\">", sigbuf);
     290                 :          0 :     cgi.sig = logpath->s;
     291                 :          0 :     ZX_FREE(cf->ctx, logpath);
     292                 :            :   }
     293                 :          5 :   payload = zxid_template_page_cf(cf, &cgi, cf->post_templ_file, cf->post_templ, 64*1024, 0);
     294                 :            : #else
     295                 :            :   payload = zx_strf(cf->ctx, "<title>ZXID POST Profile</title>"
     296                 :            : "<body bgcolor=white OnLoad=\"document.forms[0].submit()\">"
     297                 :            : "<h1>ZXID POST Profile POST</h1>"
     298                 :            : "<form method=post action=\"%.*s\">\n"
     299                 :            : "<input type=hidden name=%s value=\"%s\"><br>\n"
     300                 :            : "%s%s%s"  /* rs */
     301                 :            : "%s%s%s"  /* sigalg & sig */
     302                 :            : "<input type=submit name=ok value=\" If JavaScript is not on, please click here to complete the transaction \">"
     303                 :            : "</form>",
     304                 :            :                     action_url->len, action_url->s,
     305                 :            :                     field, url,
     306                 :            :                     rs_len?"<input type=hidden name=RelayState value=\"":"",
     307                 :            :                     rs_len?relay_state:"",
     308                 :            :                     rs_len?"\">":"",
     309                 :            :                     sign?"<input type=hidden name=SigAlg value=\"" SIG_ALGO "\"><input type=hidden name=Signature value=\"":"",
     310                 :            :                     sigbuf,
     311                 :            :                     sign?"\">":"");
     312                 :            : #endif
     313                 :          5 :   ZX_FREE(cf->ctx, url);
     314                 :          5 :   return payload;
     315                 :            : }
     316                 :            : 
     317                 :            : struct zx_str zxstr_unknown = {0,0,sizeof("UNKNOWN")-1, "UNKNOWN"};
     318                 :            : 
     319                 :            : /*(i) Encode and sign a URL according to SAML2 redirect binding.
     320                 :            :  * zxid_decode_redir_or_post() performs the opposite operation.
     321                 :            :  *
     322                 :            :  * 1. Compress payload
     323                 :            :  * 2. Base64 encode payload
     324                 :            :  * 3. URL encode and concatenate RelayState (if any)
     325                 :            :  * 4. Sign the URL encoded form (SimpleSign signs message prior to base64 and URL encodings)
     326                 :            :  * 5. Base64 encode the sig and concatenate to the URL
     327                 :            :  *
     328                 :            :  * cf::          ZXID configuration object, also used for memory allocation
     329                 :            :  * field::       The name of the CGI variable, e.g. "SAMLRequest=" or "SAMLResponse="
     330                 :            :  * payload::     What should be encoded in the redirect URL. Effectively becomes the query string
     331                 :            :  * relay_state:: Optional relay state argument. Ends up being encoded in the query string
     332                 :            :  * return::      Query string encoding of the request. The memory should be freed by the caller. */
     333                 :            : 
     334                 :            : /* Called by:  zxid_saml2_redir, zxid_saml2_redir_url, zxid_saml2_resp_redir */
     335                 :            : struct zx_str* zxid_saml2_redir_enc(zxid_conf* cf, char* field, struct zx_str* pay_load, char* relay_state)
     336                 :          9 : {
     337                 :            :   EVP_PKEY* sign_pkey;
     338                 :            :   struct zx_str* logpath;
     339                 :            :   struct zx_str* ss;
     340                 :            :   char* zbuf;
     341                 :            :   char* b64;
     342                 :            :   char* url;
     343                 :            :   char* sig;
     344                 :            :   char* p;
     345                 :            :   int zlen, len, slen, field_len, rs_len;
     346                 :          9 :   field_len = strlen(field);
     347         [ +  + ]:          9 :   rs_len = relay_state?strlen(relay_state):0;
     348                 :            :   
     349                 :            :   /* RFC1951 per SAML2 binding line 576 (p.17), i.e. NOT gzip or ordinary zlib */
     350                 :          9 :   zbuf = zx_zlib_raw_deflate(cf->ctx, pay_load->len, pay_load->s, &zlen);
     351         [ -  + ]:          9 :   if (!zbuf)
     352                 :          0 :     return 0;
     353                 :            :   
     354                 :          9 :   len = SIMPLE_BASE64_LEN(zlen);
     355                 :          9 :   b64 = ZX_ALLOC(cf->ctx, len);
     356                 :          9 :   p = base64_fancy_raw(zbuf, zlen, b64, std_basis_64, 1<<31, 0, 0, '=');
     357                 :            :   
     358                 :          9 :   len = field_len + zx_url_encode_len(p-b64, b64) - 1 /* zap nul termination */;
     359         [ +  + ]:          9 :   url = ZX_ALLOC(cf->ctx, len + sizeof("&SigAlg=" SIG_ALGO_URLENC)
     360                 :            :                  + (rs_len?(sizeof("&RelayState=")-1+rs_len):0));
     361                 :          9 :   memcpy(url, field, field_len);
     362                 :            : 
     363                 :          9 :   zx_url_encode_raw(p-b64, b64, url+field_len);
     364                 :          9 :   ZX_FREE(cf->ctx, b64);
     365                 :            :   
     366         [ +  + ]:          9 :   if (rs_len) {
     367                 :          3 :     memcpy(url + len, "&RelayState=", sizeof("&RelayState=")-1);
     368                 :          3 :     memcpy(url + len + sizeof("&RelayState=")-1, relay_state, rs_len);
     369                 :          3 :     len += sizeof("&RelayState=")-1+rs_len;
     370                 :            :   }
     371                 :            :   
     372         [ -  + ]:          9 :   if (!cf->authn_req_sign) {    /* Simple nonsigned case. */
     373                 :          0 :     url[len] = 0;  /* Reservation for ETSIG_ALGO_RSA_SHA1_URLENC provides space for nul term. */
     374                 :          0 :     return zx_ref_len_str(cf->ctx, len, url);
     375                 :            :   }
     376                 :            :   
     377                 :            :   /* Additional URL signing */
     378                 :            :   
     379                 :          9 :   memcpy(url+len, "&SigAlg=" SIG_ALGO_URLENC, sizeof("&SigAlg=" SIG_ALGO_URLENC)-1);
     380                 :          9 :   len += sizeof("&SigAlg=" SIG_ALGO_URLENC)-1;
     381         [ +  - ]:          9 :   if (zxid_lazy_load_sign_cert_and_pkey(cf, 0, &sign_pkey, "SAML2 redir"))
     382                 :          9 :     zlen = zxsig_data(cf->ctx, len, url, &zbuf, sign_pkey, "SAML2 redir");
     383         [ -  + ]:          9 :   if (zlen == -1)
     384                 :          0 :     return 0;
     385                 :            :   
     386                 :            :   /* Base64 and URL encode the sig. Had SAML2 specified safe base64, world would be simpler! */
     387                 :            :   
     388                 :          9 :   b64 = ZX_ALLOC(cf->ctx, SIMPLE_BASE64_LEN(zlen));
     389                 :          9 :   p = base64_fancy_raw(zbuf, zlen, b64, std_basis_64, 1<<31, 0, 0, '=');
     390                 :            :   
     391                 :          9 :   slen = zx_url_encode_len(p-b64, b64) - 1;
     392                 :          9 :   sig = ZX_ALLOC(cf->ctx, len + sizeof(ETSIGNATURE_EQ)-1 + slen + 1);
     393                 :          9 :   memcpy(sig, url, len);
     394                 :          9 :   memcpy(sig + len, ETSIGNATURE_EQ, sizeof(ETSIGNATURE_EQ)-1);
     395                 :          9 :   len += sizeof(ETSIGNATURE_EQ)-1;
     396                 :          9 :   zx_url_encode_raw(p-b64, b64, sig + len);
     397                 :          9 :   ZX_FREE(cf->ctx, b64);
     398                 :          9 :   ZX_FREE(cf->ctx, url);
     399                 :          9 :   sig[len + slen] = 0;
     400                 :            :   
     401                 :          9 :   ss = zx_ref_len_str(cf->ctx, len + slen, sig);
     402                 :            : 
     403         [ +  - ]:          9 :   if (cf->log_issue_msg) {
     404                 :          9 :     logpath = zxlog_path(cf, &zxstr_unknown, ss, ZXLOG_ISSUE_DIR, ZXLOG_WIR_KIND, 1);
     405         [ +  - ]:          9 :     if (logpath) {
     406         [ -  + ]:          9 :       if (zxlog_dup_check(cf, logpath, "Redir")) {
     407                 :          0 :         ERR("Duplicate wire msg(%.*s) (Redir)", ss->len, ss->s);
     408         [ #  # ]:          0 :         if (cf->dup_msg_fatal) {
     409                 :          0 :           ERR("FATAL (by configuration): Duplicate wire msg(%.*s) (Redir)", ss->len, ss->s);
     410                 :          0 :           zxlog_blob(cf, 1, logpath, ss, "Redir dup");
     411                 :          0 :           zx_str_free(cf->ctx, logpath);
     412                 :          0 :           ZX_FREE(cf->ctx, ss);
     413                 :          0 :           return 0;
     414                 :            :         }
     415                 :            :       }
     416                 :          9 :       zxlog_blob(cf, 1, logpath, ss, "Redir");
     417                 :          9 :       zx_str_free(cf->ctx, logpath);
     418                 :            :     }
     419                 :            :   }
     420                 :            : 
     421                 :          9 :   return ss;
     422                 :            : }
     423                 :            : 
     424                 :            : /*() SAMLRequest. Return the URL needed for redirect. You need to pass this to
     425                 :            :  * some application layer facility to effectuate the actual redirect.
     426                 :            :  * Wrapper for zxid_saml2_redir_enc(). This function is different from
     427                 :            :  * zxid_saml2_redir() in that only the URL is returned, not the complete
     428                 :            :  * Location header.
     429                 :            :  *
     430                 :            :  * cf::          ZXID configuration object, also used for memory allocation
     431                 :            :  * loc::         The URL up to query string
     432                 :            :  * pay_load::    What should be encoded in the redirect URL. Effectively becomes the query string
     433                 :            :  * relay_state:: Optional relay state argument. Ends up being encoded in the query string
     434                 :            :  * return::      URL suitable for redirection as ~zx_str~. The memory should be freed by the caller. */
     435                 :            : 
     436                 :            : /* Called by:  zxid_start_sso_url */
     437                 :            : struct zx_str* zxid_saml2_redir_url(zxid_conf* cf, struct zx_str* loc, struct zx_str* pay_load, char* relay_state)
     438                 :          5 : {
     439                 :            :   struct zx_str* ss;
     440                 :          5 :   struct zx_str* rse = zxid_saml2_redir_enc(cf, "SAMLRequest=", pay_load, relay_state);
     441   [ +  -  -  + ]:          5 :   if (!loc || !rse) {
     442   [ #  #  #  #  :          0 :     ERR("Redirection location URL missing. rse(%.*s) %p", rse?rse->len:0, rse?STRNULLCHK(rse->s):"", rse);
                   #  # ]
     443                 :          0 :     return 0;
     444                 :            :   }
     445         [ +  - ]:          5 :   ss = zx_strf(cf->ctx, (memchr(loc->s, '?', loc->len)
     446                 :            :                          ? "%.*s&%.*s" CRLF2
     447                 :            :                          : "%.*s?%.*s" CRLF2), loc->len, loc->s, rse->len, rse->s);
     448         [ -  + ]:          5 :   if (zx_debug & ZXID_INOUT) INFO("%.*s", ss->len, ss->s);
     449                 :          5 :   zx_str_free(cf->ctx, rse);
     450                 :          5 :   return ss;
     451                 :            : }
     452                 :            : 
     453                 :            : /*() SAMLRequest. Return the HTTP 302 redirect LOCATION header + CRLF2. You need to pass this to
     454                 :            :  * some application layer facility to effectuate the actual redirect.
     455                 :            :  * Wrapper for zxid_saml2_redir_enc(). This is different from zxid_saml2_redir_url()
     456                 :            :  * in that the entire Location header is returned, rather than just the url.
     457                 :            :  *
     458                 :            :  * cf::          ZXID configuration object, also used for memory allocation
     459                 :            :  * loc::         The URL up to query string
     460                 :            :  * pay_load::    What should be encoded in the redirect URL. Effectively becomes the query string
     461                 :            :  * relay_state:: Optional relay state argument. Ends up being encoded in the query string
     462                 :            :  * return::      HTTP Location header as ~zx_str~. The memory should be freed by the caller. */
     463                 :            : 
     464                 :            : /* Called by:  zxid_sp_mni_redir, zxid_sp_slo_redir */
     465                 :            : struct zx_str* zxid_saml2_redir(zxid_conf* cf, struct zx_str* loc, struct zx_str* pay_load, char* relay_state)
     466                 :          2 : {
     467                 :            :   struct zx_str* ss;
     468                 :          2 :   struct zx_str* rse = zxid_saml2_redir_enc(cf, "SAMLRequest=", pay_load, relay_state);
     469   [ +  -  -  + ]:          2 :   if (!loc || !rse) {
     470   [ #  #  #  #  :          0 :     ERR("Redirection location URL missing. rse(%.*s) %p", rse?rse->len:0, rse?STRNULLCHK(rse->s):"", rse);
                   #  # ]
     471                 :          0 :     return zx_dup_str(cf->ctx, "* ERR");
     472                 :            :   }
     473         [ +  - ]:          2 :   ss = zx_strf(cf->ctx, (memchr(loc->s, '?', loc->len)
     474                 :            :                          ? "Location: %.*s&%.*s" CRLF2
     475                 :            :                          : "Location: %.*s?%.*s" CRLF2), loc->len, loc->s, rse->len, rse->s);
     476         [ -  + ]:          2 :   if (zx_debug & ZXID_INOUT) INFO("%.*s", ss->len - sizeof(CRLF2) + 1, ss->s);
     477                 :          2 :   zx_str_free(cf->ctx, rse);
     478                 :          2 :   return ss;
     479                 :            : }
     480                 :            : 
     481                 :            : /*() SAMLResponse. Return the HTTP 302 redirect LOCATION header + CRLF2. You
     482                 :            :  * need to pass this to some application layer facility to effectuate the actual redirect.
     483                 :            :  * Wrapper for zxid_saml2_redir_enc().
     484                 :            :  *
     485                 :            :  * cf::          ZXID configuration object, also used for memory allocation
     486                 :            :  * loc::         The URL up to query string
     487                 :            :  * pay_load::    What should be encoded in the redirect URL. Effectively becomes the query string
     488                 :            :  * relay_state:: Optional relay state argument. Ends up being encoded in the query string
     489                 :            :  * return::      HTTP Location header as ~zx_str~. The memory should be freed by the caller. */
     490                 :            : 
     491                 :            : /* Called by:  zxid_idp_dispatch, zxid_slo_resp_redir, zxid_sp_dispatch */
     492                 :            : struct zx_str* zxid_saml2_resp_redir(zxid_conf* cf, struct zx_str* loc, struct zx_str* pay_load, char* relay_state)
     493                 :          2 : {
     494                 :            :   struct zx_str* ss;
     495                 :          2 :   struct zx_str* rse = zxid_saml2_redir_enc(cf, "SAMLResponse=", pay_load, relay_state);
     496   [ +  -  -  + ]:          2 :   if (!loc || !rse) {
     497   [ #  #  #  #  :          0 :     ERR("Redirection location(%.*s) URL missing or redirect encoding(%.*s) failed.", loc?loc->len:0, loc?loc->s:"", rse?rse->len:0, rse?rse->s:"");
             #  #  #  # ]
     498                 :          0 :     return zx_dup_str(cf->ctx, "* ERR");
     499                 :            :   }
     500         [ +  - ]:          2 :   ss = zx_strf(cf->ctx, (memchr(loc->s, '?', loc->len)
     501                 :            :                          ? "Location: %.*s&%.*s" CRLF2
     502                 :            :                          : "Location: %.*s?%.*s" CRLF2), loc->len, loc->s, rse->len, rse->s);
     503         [ -  + ]:          2 :   if (zx_debug & ZXID_INOUT) INFO("%.*s", ss->len - sizeof(CRLF2) + 1, ss->s);
     504                 :          2 :   zx_str_free(cf->ctx, rse);
     505                 :          2 :   return ss;
     506                 :            : }
     507                 :            : 
     508                 :            : /*() Check status codes in SAML response to verify that request was completed OK.
     509                 :            :  *
     510                 :            :  * cf::     ZXID configuration object, also used for memory allocation
     511                 :            :  * cgi::    CGI variables decoded from the query string. ~err~ field of
     512                 :            :  *     the CGI object will be set upon failure.
     513                 :            :  * st::     The SAML <Status> element from the response, as XML data structure
     514                 :            :  * what::   Explanatory string used in error and log messages
     515                 :            :  * return:: 1 of SAML message is OK, 0 if message is not OK. */
     516                 :            : 
     517                 :            : /* Called by:  zxid_az_soap, zxid_idp_dispatch x2, zxid_idp_soap_dispatch, zxid_sp_dispatch x3, zxid_sp_mni_soap, zxid_sp_slo_soap, zxid_sp_soap_dispatch x3 */
     518                 :            : int zxid_saml_ok(zxid_conf* cf, zxid_cgi* cgi, struct zx_sp_Status_s* st, char* what)
     519                 :         95 : {
     520                 :            :   struct zx_str* ss;
     521                 :         95 :   struct zx_str* m = 0;
     522                 :         95 :   struct zx_str* sc1 = 0;
     523                 :         95 :   struct zx_str* sc2 = 0;
     524                 :         95 :   struct zx_sp_StatusCode_s* sc = st->StatusCode;
     525         [ +  - ]:         95 :   if (!memcmp(SAML2_SC_SUCCESS, sc->Value->g.s, sc->Value->g.len)) {
     526   [ +  +  -  + ]:         95 :     D("SAML ok what(%s)", what);
     527         [ +  - ]:         95 :     if (cf->log_level>0)
     528                 :         95 :       zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "K", "SAMLOK", what, 0);
     529                 :         95 :     return 1;
     530                 :            :   }
     531   [ #  #  #  #  :          0 :   if (st->StatusMessage && (m = ZX_GET_CONTENT(st->StatusMessage)))
          #  #  #  #  #  
                      # ]
     532                 :          0 :     ERR("SAML Fail what(%s) msg(%.*s)", what, m->len, m->s);
     533         [ #  # ]:          0 :   if (sc1 = &sc->Value->g)
     534                 :          0 :     ERR("SAML Fail what(%s) SC1(%.*s)", what, sc1->len, sc1->s);
     535         [ #  # ]:          0 :   if (sc->StatusCode)
     536                 :          0 :     sc2 = &sc->StatusCode->Value->g;
     537         [ #  # ]:          0 :   for (sc = sc->StatusCode; sc; sc = sc->StatusCode)
     538                 :          0 :     ERR("SAML Fail what(%s) subcode(%.*s)", what, sc->Value->g.len, sc->Value->g.s);
     539                 :            :     
     540   [ #  #  #  #  :          0 :   ss = zx_strf(cf->ctx, "SAML Fail what(%s) msg(%.*s) SC1(%.*s) subcode(%.*s)", what,
          #  #  #  #  #  
                #  #  # ]
     541                 :            :                m?m->len:0, m?m->s:"",
     542                 :            :                sc1?sc1->len:0, sc1?sc1->s:"",
     543                 :            :                sc2?sc2->len:0, sc2?sc2->s:"");
     544                 :            : 
     545         [ #  # ]:          0 :   if (cf->log_level>0)
     546                 :          0 :     zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "F", "SAMLFAIL", what, ss->s);
     547                 :            :   
     548   [ #  #  #  # ]:          0 :   D("SAML Response NOT OK what(%s)", what);
     549         [ #  # ]:          0 :   if (!cgi)
     550                 :          0 :     return 0;
     551                 :          0 :   cgi->err = ss->s;
     552                 :          0 :   return 0;
     553                 :            : }
     554                 :            : 
     555                 :            : /*() Given NameID or <EncryptedID>, return Name ID. Typically used by SSO and SLO.
     556                 :            :  * If unencrypted NameID is available, then decryption will not be attempted.
     557                 :            :  * This facilitates code that handles either encrypted or non-encrypted
     558                 :            :  * case in one line:
     559                 :            :  *
     560                 :            :  *   req->NameID = zxid_decrypt_nameid(cf, req->NameID, req->EncryptedID);
     561                 :            :  *
     562                 :            :  * cf::     ZXID configuration object, also used for memory allocation
     563                 :            :  * nid::    XML data structure for Name ID. Possibly 0 (NULL). In that case ~encid~
     564                 :            :  *     should be specified.
     565                 :            :  * encid::  XML Data Structure for Encrypted Name ID. If no ~nid~ is specified, this
     566                 :            :  *     structure is decrypted and its contents returned as the Name ID
     567                 :            :  * return:: XML data structure corresponding to (possibly decrypted) Name ID */
     568                 :            : 
     569                 :            : /* Called by:  test_ibm_cert_problem, test_ibm_cert_problem_enc_dec, zxid_idp_slo_do, zxid_imreq, zxid_mni_do, zxid_nidmap_do, zxid_sp_slo_do, zxid_sp_sso_finalize, zxid_wsf_validate_a7n */
     570                 :            : zxid_nid* zxid_decrypt_nameid(zxid_conf* cf, zxid_nid* nid, struct zx_sa_EncryptedID_s* encid)
     571                 :         49 : {
     572                 :            :   struct zx_str* ss;
     573                 :            :   struct zx_root_s* r;
     574         [ +  + ]:         49 :   if (nid)
     575                 :          5 :     return nid;
     576         [ +  - ]:         44 :   if (encid) {
     577                 :         44 :     ss = zxenc_privkey_dec(cf, encid->EncryptedData, encid->EncryptedKey);
     578         [ -  + ]:         44 :     if (!ss) {
     579                 :          0 :       ERR("Failed to decrypt NameID. Most probably certificate-private key mismatch or metadata problem. Could also be corrupt message. %d", 0);
     580                 :          0 :       return 0;
     581                 :            :     }
     582                 :         44 :     r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "dec nid");
     583         [ -  + ]:         44 :     if (!r) {
     584                 :          0 :       ERR("Failed to parse EncryptedID buf(%.*s)", ss->len, ss->s);
     585                 :          0 :       return 0;
     586                 :            :     }
     587                 :         44 :     return r->NameID;
     588                 :            :   }
     589                 :          0 :   ERR("Neither NameID nor EncryptedID available %d", 0);
     590                 :          0 :   return 0;
     591                 :            : }
     592                 :            : 
     593                 :            : /*() Given new nym or <NewEncryptedID>, return Name ID. Typically used by Name ID Management
     594                 :            :  *
     595                 :            :  * cf::     ZXID configuration object, also used for memory allocation
     596                 :            :  * newnym:: XML data structure for new Name ID. Possibly 0 (NULL). In that case ~encid~
     597                 :            :  *     should be specified.
     598                 :            :  * encid::  XML Data Structure for Encrypted Name ID. If no ~newnym~ is specified, this
     599                 :            :  *     structure is decrypted and its contents returned as the Name ID
     600                 :            :  * return:: XML data structure corresponding to (possibly decrypted) new Name ID */
     601                 :            : 
     602                 :            : /* Called by:  zxid_mni_do */
     603                 :            : struct zx_str* zxid_decrypt_newnym(zxid_conf* cf, struct zx_str* newnym, struct zx_sp_NewEncryptedID_s* encid)
     604                 :          1 : {
     605                 :            :   struct zx_str* ss;
     606                 :            :   struct zx_root_s* r;
     607         [ -  + ]:          1 :   if (newnym)
     608                 :          0 :     return newnym;
     609         [ +  - ]:          1 :   if (encid) {
     610                 :          1 :     ss = zxenc_privkey_dec(cf, encid->EncryptedData, encid->EncryptedKey);
     611                 :          1 :     r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "dec newnym");
     612         [ -  + ]:          1 :     if (!r) {
     613                 :          0 :       ERR("Failed to parse NewEncryptedID buf(%.*s)", ss->len, ss->s);
     614                 :          0 :       return 0;
     615                 :            :     }
     616   [ +  -  +  -  :          1 :     return ZX_GET_CONTENT(r->NewID);
                   +  - ]
     617                 :            :   }
     618                 :          0 :   ERR("Neither NewNameID nor NewEncryptedID available %d", 0);
     619                 :          0 :   return 0;
     620                 :            : }
     621                 :            : 
     622                 :            : /*(i) Check single item signature on given Request, Response, or Assertion. Typical usage
     623                 :            :  *
     624                 :            :  *     if (!zxid_chk_sig(cf, cgi, ses, (struct zx_elem_s*)req,
     625                 :            :  *                       req->Signature, req->Issuer, "LogoutRequest"))
     626                 :            :  *       return 0;
     627                 :            :  *
     628                 :            :  * cf:: ZXID configuration and context object, used for settings and memory allocation
     629                 :            :  * cgi:: cgi or invocation variables object. cgi->sigval and cgi->sigmsg
     630                 :            :  *     will be altered, if there is any signature.
     631                 :            :  * ses:: Session object. The ses->sigres will be altered to reflect result
     632                 :            :  *     of verification, if there is signature.
     633                 :            :  * elem:: Element that was signed, usually needs type cast.
     634                 :            :  * sig:: Signature element within elem
     635                 :            :  * issue_ent:: The EntityID zx_str of the signer (Issuer)
     636                 :            :  * pop_seen:: Namespaces collected from outer layers
     637                 :            :  * lk:: Log key
     638                 :            :  * return:: 0 if sig check could not be made due to error, 1 if there was
     639                 :            :  *     no signature to check, 2 if check was made, in which case the result is
     640                 :            :  *     in ses->sigres, 3 if check was not possible (due to error), but sig was not
     641                 :            :  *     configured to be required (NOSIG_FATAL option).
     642                 :            :  *
     643                 :            :  * See also: Signature validation codes VVV in zxid-log.pd, section "ZXID Log Format".
     644                 :            :  * N.B: If the signature is over multiple references, you need to do many processing steps
     645                 :            :  * manually and then call zxsig_validate() with correctly populate refs array.
     646                 :            :  */
     647                 :            : 
     648                 :            : /* Called by:  sig_validate, zxid_idp_slo_do, zxid_mni_do, zxid_sp_dig_sso_a7n, zxid_sp_slo_do, zxid_xacml_az_cd1_do, zxid_xacml_az_do */
     649                 :            : int zxid_chk_sig(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_elem_s* elem, struct zx_ds_Signature_s* sig, struct zx_sa_Issuer_s* issue_ent, struct zx_ns_s* pop_seen, const char* lk)
     650                 :        112 : {
     651                 :        112 :   struct zx_str* issuer = 0;
     652                 :            :   struct zxsig_ref refs;
     653                 :            :   zxid_entity* idp_meta;
     654                 :        112 :   char* err = "S"; /* See: RES in zxid-log.pd, section "ZXID Log Format" */
     655                 :            :   
     656   [ +  +  +  +  :        112 :   if (!sig) { D("No signature in %s", lk); return 1; /* Not an error */ }
                   -  + ]
     657   [ +  -  -  + ]:        101 :   if (!sig->SignedInfo || !sig->SignedInfo->Reference) {
     658                 :          0 :     ERR("Malformed signature in %s, missing mandatory SignedInfo(%p) or Reference", lk, sig->SignedInfo);
     659                 :          0 :     cgi->sigval = "M";
     660                 :          0 :     cgi->sigmsg = "Malformed signature.";
     661                 :          0 :     ses->sigres = ZXSIG_NO_SIG;
     662                 :          0 :     err = "C";
     663                 :          0 :     goto erro;
     664                 :            :   }
     665                 :            : 
     666   [ +  -  +  -  :        101 :   if (!issue_ent || !(issuer = ZX_GET_CONTENT(issue_ent)) || !issuer->len || !issuer->s[0]) {
          +  -  +  -  +  
             -  +  -  -  
                      + ]
     667                 :          0 :     ERR("Issuer of %s is empty although %s was signed. %p", lk, lk, issuer);
     668                 :          0 :     cgi->sigval = "I";
     669                 :          0 :     cgi->sigmsg = "Issuer of signed Response missing.";
     670                 :          0 :     ses->sigres = ZXSIG_NO_SIG;
     671         [ #  # ]:          0 :     if (!cf->nosig_fatal)
     672                 :          0 :       goto nosig_allow;
     673                 :          0 :     err = "C";
     674                 :          0 :     goto erro;
     675                 :            :   }
     676                 :            :   
     677                 :        101 :   idp_meta = zxid_get_ent_ss(cf, issuer);
     678         [ -  + ]:        101 :   if (!idp_meta) {
     679                 :          0 :     ERR("Unable to find metadata for Issuer(%.*s).", issuer->len, issuer->s);
     680                 :          0 :     cgi->sigval = "I";
     681                 :          0 :     cgi->sigmsg = "Issuer of signed Response unknown.";
     682                 :          0 :     ses->sigres = ZXSIG_NO_SIG;
     683         [ #  # ]:          0 :     if (!cf->nosig_fatal)
     684                 :          0 :       goto nosig_allow;
     685                 :          0 :     err = "P";  /* Policy issue */
     686                 :          0 :     goto erro;
     687                 :            :   }
     688                 :            : 
     689                 :        101 :   ZERO(&refs, sizeof(refs));
     690                 :        101 :   refs.sref = sig->SignedInfo->Reference;
     691                 :        101 :   refs.blob = elem;
     692                 :        101 :   refs.pop_seen = pop_seen;
     693                 :        101 :   zx_see_elem_ns(cf->ctx, &refs.pop_seen, elem);
     694                 :        101 :   ses->sigres = zxsig_validate(cf->ctx, idp_meta->sign_cert, sig, 1, &refs);
     695                 :        101 :   zxid_sigres_map(ses->sigres, &cgi->sigval, &cgi->sigmsg);
     696   [ +  +  -  + ]:        101 :   D("Response sigres(%d)", ses->sigres);
     697                 :        101 :   return 2;
     698                 :            : 
     699                 :          0 : nosig_allow:
     700                 :          0 :   return 3;
     701                 :            : 
     702                 :          0 : erro:
     703                 :          0 :   cgi->msg = "SSO failed due to Response that was signed, but badly (or did not have Issuer).";
     704   [ #  #  #  # ]:          0 :   zxlog(cf, 0, 0, 0, issuer, 0, 0, 0,
     705                 :            :         cgi->sigval, err, ses->nidfmt?"FEDSSO":"TMPSSO", ses->sesix?ses->sesix:"-", "Error.");
     706                 :          0 :   return 0;
     707                 :            : }
     708                 :            : 
     709                 :            : /*() Figure out sp_name_buf corresponding to affiliation */
     710                 :            : 
     711                 :            : /* Called by:  zxid_add_fed_tok2epr, zxid_map_val_ss x3 */
     712                 :            : struct zx_str* zxid_get_affil_and_sp_name_buf(zxid_conf* cf, zxid_entity* meta, char* sp_name_buf)
     713                 :       1762 : {
     714                 :            :   struct zx_str* affil;
     715   [ +  -  +  -  :       1762 :   if (meta && meta->ed && meta->ed->AffiliationDescriptor
          -  +  #  #  #  
                #  #  # ]
     716                 :            :       && (affil = &meta->ed->AffiliationDescriptor->affiliationOwnerID->g)
     717                 :            :       && affil->s && affil->len)
     718                 :            :     ; /* affil is good */
     719                 :            :   else
     720   [ +  -  +  - ]:       1762 :     affil = meta && meta->ed ? &meta->ed->entityID->g : 0;
     721         [ -  + ]:       1762 :   if (!affil) {
     722         [ #  # ]:          0 :     ERR("Unable to determine affiliation ID or provider ID. Metadata missing? %p %p", meta, meta?meta->ed:0);
     723                 :          0 :     *sp_name_buf = 0;
     724                 :          0 :     return 0;
     725                 :            :   }
     726                 :       1762 :   zxid_nice_sha1(cf, sp_name_buf, ZXID_MAX_SP_NAME_BUF, affil, affil, 7);
     727                 :       1762 :   return affil;
     728                 :            : }
     729                 :            : 
     730                 :            : /* Called by:  zxid_add_fed_tok2epr, zxid_map_val_ss x2, zxid_sso_issue_a7n */
     731                 :            : zxid_nid* zxid_get_fed_nameid(zxid_conf* cf, struct zx_str* prvid, struct zx_str* affil, const char* uid, const char* sp_name_buf, int allow_create, int want_transient, struct timeval* srcts, struct zx_str* id, char* logop)
     732                 :       1225 : {
     733                 :       1225 :   zxid_nid* nameid = zxid_check_fed(cf, affil, uid, allow_create, srcts, prvid, id, sp_name_buf);
     734         [ +  + ]:       1225 :   if (nameid) {
     735         [ -  + ]:       1224 :     if (want_transient) {
     736   [ #  #  #  # ]:          0 :       D("Despite old fed, using transient due to want_transient=%d", want_transient);
     737                 :          0 :       zxid_mk_transient_nid(cf, nameid, sp_name_buf, uid);
     738         [ #  # ]:          0 :       if (logop) strcpy(logop, "TMPDI");
     739                 :            :     } else
     740         [ +  + ]:       1224 :       if (logop) strcpy(logop, "FEDDI");
     741                 :            :   } else {
     742   [ -  +  #  # ]:          1 :     D("No nameid (because of no federation), using transient %d", 0);
     743                 :          1 :     nameid = zx_NEW_sa_NameID(cf->ctx,0);
     744                 :          1 :     zxid_mk_transient_nid(cf, nameid, sp_name_buf, uid);
     745         [ -  + ]:          1 :     if (logop) strcpy(logop, "TMPDI");
     746                 :            :   }
     747                 :       1225 :   return nameid;
     748                 :            : }
     749                 :            : 
     750                 :            : /*() Transform content according to map. The returned zx_str will be nul terminated.
     751                 :            :  * The list ends up being built in reverse order, which at runtime
     752                 :            :  * causes last stanzas to be evaluated first and first match is used.
     753                 :            :  * Thus you should place most specific rules last and most generic rules first.
     754                 :            :  * See also: zxid_load_map() and zxid_find_map() */
     755                 :            : 
     756                 :            : /* Called by:  zxid_add_at_values, zxid_map_val */
     757                 :            : struct zx_str* zxid_map_val_ss(zxid_conf* cf, zxid_ses* ses, zxid_entity* meta, struct zxid_map* map, const char* atname, struct zx_str* val)
     758                 :       8163 : {
     759                 :            :   zxid_a7n* a7n;
     760                 :            :   zxid_nid* nameid;
     761                 :            :   struct zx_sa_AttributeStatement_s* at_stmt;
     762                 :            :   struct zx_str* prvid;
     763                 :            :   struct zx_str* affil;
     764                 :            :   struct zx_str* ss;
     765                 :            :   char buf[MIN(ZXID_MAX_SP_NAME_BUF,4096)];
     766                 :            :   char* bin;
     767                 :            :   char* p;
     768                 :            :   int len;
     769         [ -  + ]:       8163 :   if (!val) {
     770                 :          0 :     ERR("NULL ponter as val %p", map);
     771                 :          0 :     val = zx_dup_str(cf->ctx, "");
     772                 :            :   }
     773         [ +  + ]:       8163 :   if (!map)
     774                 :       1062 :     return val;
     775                 :            : 
     776   [ +  +  +  +  :       7101 :   switch (map->rule & ZXID_MAP_RULE_WRAP_MASK) {
                      - ]
     777                 :       5469 :   case 0: break; /* No wrap */
     778                 :            :   case ZXID_MAP_RULE_WRAP_A7N:   /* 0x10 Wrap the attribute in SAML2 assertion */
     779   [ +  -  +  -  :        680 :     if (!meta || !ses || !ses->uid) {
                   -  + ]
     780                 :          0 :       ERR("MAP_RULE_WRAP_A7N requires SP metadata and session to be specified. %p %p", meta, ses);
     781                 :          0 :       break;
     782                 :            :     }
     783                 :        680 :     affil = zxid_get_affil_and_sp_name_buf(cf, meta, buf);
     784         [ +  - ]:        680 :     prvid = meta->ed ? &meta->ed->entityID->g : 0;
     785                 :        680 :     nameid = zxid_get_fed_nameid(cf, prvid, affil, ses->uid, buf, cf->di_allow_create,
     786                 :            :                                  (cf->di_nid_fmt == 't'), 0, 0, 0);
     787                 :            : 
     788                 :        680 :     at_stmt = zx_NEW_sa_AttributeStatement(cf->ctx, 0);
     789   [ +  -  +  +  :        680 :     if (map->dst && *map->dst && map->src && map->src[0] != '*')
             +  -  +  - ]
     790                 :        136 :       atname = map->dst;
     791                 :        680 :     at_stmt->Attribute = zxid_mk_sa_attribute_ss(cf, &at_stmt->gg, atname, 0, val);
     792                 :            : 
     793                 :        680 :     a7n = zxid_mk_a7n(cf, prvid, zxid_mk_subj(cf, 0, meta, nameid), 0, at_stmt);
     794                 :        680 :     zxid_anoint_a7n(cf, 1, a7n, prvid, "map_val", ses->uid);
     795                 :        680 :     val = zx_easy_enc_elem_opt(cf, &a7n->gg);
     796                 :        680 :     break;
     797                 :            :   case ZXID_MAP_RULE_WRAP_X509:  /* 0x20 Wrap the attribute in X509 attribute certificate */
     798   [ +  -  +  -  :        408 :     if (!meta || !ses || !ses->uid) {
                   -  + ]
     799                 :          0 :       ERR("MAP_RULE_WRAP_X509 requires SP metadata and session to be specified. %p %p", meta, ses);
     800                 :          0 :       break;
     801                 :            :     }
     802                 :        408 :     affil = zxid_get_affil_and_sp_name_buf(cf, meta, buf);
     803         [ +  - ]:        408 :     prvid = meta->ed ? &meta->ed->entityID->g : 0;
     804                 :        408 :     nameid = zxid_get_fed_nameid(cf, prvid, affil, ses->uid, buf, cf->di_allow_create,
     805                 :            :                                  (cf->di_nid_fmt == 't'), 0, 0, 0);
     806                 :            : 
     807   [ +  -  -  +  :        408 :     if (map->dst && *map->dst && map->src && map->src[0] != '*')
             #  #  #  # ]
     808                 :          0 :       atname = map->dst;
     809                 :        408 :     zxid_mk_at_cert(cf, sizeof(buf), buf, "map_val", nameid, atname, val);
     810                 :        408 :     val = zx_dup_str(cf->ctx, buf);
     811                 :        408 :     break;
     812                 :            :   case ZXID_MAP_RULE_WRAP_FILE:  /* 0x30 Get attribute value from file specified in ext */
     813   [ +  -  +  -  :        544 :     if (!meta || !ses || !ses->uid) {
                   -  + ]
     814                 :          0 :       ERR("MAP_RULE_WRAP_FILE requires SP metadata and session to be specified. %p %p", meta, ses);
     815                 :          0 :       break;
     816                 :            :     }
     817                 :        544 :     zxid_get_affil_and_sp_name_buf(cf, meta, buf);
     818   [ +  -  -  + ]:        544 :     if (!map->ext || !*map->ext) {
     819                 :          0 :       ERR("WRAP_FILE rule without file name in ext field of stanza %p", map->ext);
     820                 :          0 :       break;
     821         [ -  + ]:        544 :     } else if (!strcmp(map->ext, "_VAL_FROM_FILE")) {
     822   [ #  #  #  # ]:          0 :       D("_VAL_FROM_FILE specified, taking filename from attribute value(%.*s)", val->len, val->s);
     823                 :          0 :       p = zx_str_to_c(cf->ctx, val);
     824                 :            :     } else {
     825                 :        544 :       p = map->ext;
     826                 :            :     }
     827                 :        544 :     bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
     828                 :            :                          "%s" ZXID_UID_DIR "%s/%s/%s", cf->path, ses->uid, buf, p);
     829         [ +  + ]:        544 :     if (!bin)
     830                 :        538 :       bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
     831                 :            :                            "%s" ZXID_UID_DIR "%s/.bs/%s", cf->path, ses->uid, p);
     832         [ +  + ]:        544 :     if (!bin)
     833                 :        402 :       bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
     834                 :            :                            "%s" ZXID_UID_DIR ".all/%s/%s", cf->path, buf, p);
     835         [ +  + ]:        544 :     if (!bin)
     836                 :        396 :       bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
     837                 :            :                            "%s" ZXID_UID_DIR ".all/.bs/%s", cf->path, p);
     838         [ +  + ]:        544 :     if (bin) {
     839                 :        284 :       val = zx_ref_len_str(cf->ctx, len, bin);
     840   [ +  -  -  + ]:        284 :       D("FILE RULE uid(%s) sp_name_buf(%s) file(%s) GOT(%.*s)",ses->uid,buf,p,val->len,val->s);
     841                 :            :     } else {
     842         [ +  - ]:        260 :       INFO("Attribute(%s) value not found in any file(%s" ZXID_UID_DIR "%s/%s/%s)", STRNULLCHKQ(atname), cf->path, ses->uid, buf, p);
     843                 :        260 :       val = zx_ref_str(cf->ctx, "");
     844                 :            :     }
     845                 :        544 :     break;
     846                 :            :   default:
     847   [ #  #  #  # ]:          0 :     NEVER("unknow map_val rule=%x", map->rule);
     848                 :            :   }
     849                 :            : 
     850   [ +  +  +  +  :       7101 :   switch (map->rule & ZXID_MAP_RULE_ENC_MASK) {
             +  +  +  - ]
     851                 :       5596 :   case ZXID_MAP_RULE_RENAME:     ss = val; break;
     852                 :            :   case ZXID_MAP_RULE_FEIDEDEC:   /* Norway */
     853                 :            :     /*  "feide": FEIDE currently (2008) stores several values in a single
     854                 :            :      *           AttributeValue element. The values are base64 encoded
     855                 :            :      *           and separated by an underscore. This decoder reverses that encoding. */
     856                 :            :     DD("*** FEIDEDEC only base64 decodes one attribute: it does not handle the concatenatenation with _ of several attributes. val_before(%.*s)", val->len, val->s);
     857                 :          3 :     ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(val->len));
     858                 :          3 :     p = unbase64_raw(val->s, val->s + val->len, ss->s, zx_std_index_64);
     859                 :          3 :     *p = 0;
     860                 :          3 :     ss->len = p - ss->s;
     861                 :          3 :     break;
     862                 :            :   case ZXID_MAP_RULE_FEIDEENC:   /* Norway */
     863                 :            :     DD("*** FEIDEENC only base64 encodes one attribute: it does not concatenate with _ several attributes. %d", 0);
     864                 :        544 :     ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_LEN(val->len));
     865                 :        544 :     base64_fancy_raw(val->s, val->len, ss->s, std_basis_64, 1<<31, 0, 0, '=');
     866                 :        544 :     break;
     867                 :            :   case ZXID_MAP_RULE_UNSB64_INF: /* Decode safebase64-inflate ([RFC3548], [RFC1951]) */
     868                 :          3 :     bin = ZX_ALLOC(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(val->len));
     869                 :          3 :     p = unbase64_raw(val->s, val->s + val->len, bin, zx_std_index_64);
     870                 :          3 :     ss = ZX_ZALLOC(cf->ctx, struct zx_str);
     871                 :          3 :     ss->s = zx_zlib_raw_inflate(cf->ctx, p-bin, bin, &ss->len);
     872                 :          3 :     ZX_FREE(cf->ctx, bin);
     873         [ -  + ]:          3 :     if (!ss->s) {
     874                 :          0 :       ss->len = 0;
     875                 :          0 :       ss->s = "";
     876                 :          0 :       return ss;    /* should return 0, but caller may be assuming this can not fail */
     877                 :            :     }
     878                 :          3 :     ss->s[ss->len] = 0;
     879                 :          3 :     break;
     880                 :            :   case ZXID_MAP_RULE_DEF_SB64:   /* Encode gzip-safebase64 ([RFC1951], [RFC3548]) */
     881                 :        544 :     bin = zx_zlib_raw_deflate(cf->ctx, val->len, val->s, &len);
     882         [ -  + ]:        544 :     if (!bin) {
     883                 :          0 :       return zx_dup_str(cf->ctx, "");
     884                 :            :     }
     885                 :        544 :     ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_LEN(len));
     886                 :        544 :     base64_fancy_raw(bin, len, ss->s, safe_basis_64, 1<<31, 0, 0, '=');
     887                 :        544 :     ZX_FREE(cf->ctx, bin);
     888                 :        544 :     break;
     889                 :            :   case ZXID_MAP_RULE_UNSB64:     /* NZ: Decode safebase64 ([RFC3548]) */
     890                 :          3 :     ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(val->len));
     891                 :          3 :     p = unbase64_raw(val->s, val->s + val->len, ss->s, zx_std_index_64);
     892                 :          3 :     *p = 0;
     893                 :          3 :     ss->len = p - ss->s;
     894                 :          3 :     break;
     895                 :            :   case ZXID_MAP_RULE_SB64:       /* NZ: Encode safebase64 ([RFC3548]) */
     896                 :        408 :     ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_LEN(val->len));
     897                 :        408 :     base64_fancy_raw(val->s, val->len, ss->s, safe_basis_64, 1<<31, 0, 0, '=');
     898                 :        408 :     break;
     899                 :            :   default:
     900   [ #  #  #  # ]:          0 :     NEVER("unknow map_val rule=%x", map->rule);
     901                 :            :   }
     902                 :       7101 :   return ss;
     903                 :            : }
     904                 :            : 
     905                 :            : /* Called by:  pool2apache x2, zxid_add_mapped_attr, zxid_pepmap_extract x2, zxid_pool_to_json x2, zxid_pool_to_ldif x2, zxid_pool_to_qs x2 */
     906                 :       7101 : struct zx_str* zxid_map_val(zxid_conf* cf, zxid_ses* ses, zxid_entity* meta, struct zxid_map* map, const char* atname, const char* val) {
     907         [ +  + ]:       7101 :   return zxid_map_val_ss(cf, ses, meta, map, atname, zx_dup_str(cf->ctx, STRNULLCHK(val)));
     908                 :            : }
     909                 :            : 
     910                 :            : /*() Extract from a string representing SOAP envelope, the payload part in the body. */
     911                 :            : 
     912                 :            : /* Called by:  zxcall_main */
     913                 :            : char* zxid_extract_body(zxid_conf* cf, char* enve)
     914                 :         16 : {
     915                 :            :   char* p;
     916                 :            :   char* q;
     917                 :            : 
     918         [ -  + ]:         16 :   if (!p)
     919                 :          0 :     goto nobody;
     920         [ +  - ]:         16 :   for (p = enve; p; p+=4) {
     921                 :         16 :     p = strstr(p, "Body");
     922         [ -  + ]:         16 :     if (!p) {
     923                 :          0 : nobody:
     924         [ #  # ]:          0 :       ERR("Response does not contain <Body> res(%s)", STRNULLCHKD(enve));
     925                 :          0 :       return 0;
     926                 :            :     }
     927   [ +  -  +  -  :         16 :     if (p > enve && ONE_OF_2(p[-1], '<', ':') && ONE_OF_5(p[4], '>', ' ', '\t', '\r', '\n'))
          +  -  +  -  -  
          +  #  #  #  #  
                   #  # ]
     928                 :            :       break; /* Opening <Body> detected. */
     929                 :            :   }
     930         [ -  + ]:         16 :   if (!p)
     931                 :          0 :     goto nobody;
     932                 :            :   
     933                 :         16 :   p = strchr(p+4, '>');  /* Scan for close of opening <Body */
     934         [ -  + ]:         16 :   if (!p)
     935                 :          0 :     goto nobody;
     936                 :            :   
     937         [ +  - ]:         16 :   for (q = ++p; q; q+=5) {
     938                 :         16 :     q = strstr(q, "Body>");
     939         [ -  + ]:         16 :     if (!q)
     940                 :          0 :       goto nobody;  /* Missing closing </Body> tag */
     941   [ +  -  -  + ]:         16 :     if (ONE_OF_2(q[-1], '<', ':'))
     942                 :            :       break;
     943                 :            :   }
     944         [ +  + ]:         16 :   for (--q; *q != '<'; --q) ;  /* Scan for the start of </Body>, skipping any namespace prefix */
     945                 :            : 
     946                 :         16 :   enve = ZX_ALLOC(cf->ctx, q-p+1);
     947                 :         16 :   memcpy(enve, p, q-p);
     948                 :         16 :   enve[q-p] = 0;
     949                 :         16 :   return enve;
     950                 :            : }
     951                 :            : 
     952                 :            : /*() Get symmetric key, generating it if necessary. */
     953                 :            : 
     954                 :            : /* Called by:  zxid_psobj_key_setup, zxlog_write_line */
     955                 :            : char* zx_get_symkey(zxid_conf* cf, const char* keyname, char* symkey)
     956                 :         24 : {
     957                 :            :   char buf[1024];
     958                 :         24 :   int um, gotall = read_all(sizeof(buf), buf, "symkey", 1, "%s" ZXID_PEM_DIR "%s", cf->path, keyname);
     959   [ +  -  +  - ]:         24 :   if (!gotall && cf->auto_cert) {
     960                 :         24 :     INFO("AUTO_CERT: generating symmetric encryption key in %s" ZXID_PEM_DIR "%s", cf->path, keyname);
     961                 :         24 :     gotall = 128 >> 3;
     962                 :         24 :     zx_rand(buf, gotall);
     963                 :         24 :     um = umask(0077);  /* Key material should be readable only by owner */
     964                 :         24 :     write_all_path_fmt("auto_cert", sizeof(buf), buf,
     965                 :            :                        "%s" ZXID_PEM_DIR "%s", cf->path, keyname, "%.*s", gotall, buf);
     966                 :         24 :     umask(um);
     967                 :            :   }
     968                 :         24 :   SHA1((unsigned char*)buf, gotall, (unsigned char*)symkey);
     969                 :         24 :   return symkey;
     970                 :            : }
     971                 :            : 
     972                 :            : /* EOF  --  zxidlib.c */

Generated by: LCOV version 1.9