LCOV - code coverage report
Current view: top level - zxid - zxiddec.c (source / functions) Hit Total Coverage
Test: ZXID Code Coverage Lines: 83 120 69.2 %
Date: 2010-12-19 Functions: 2 2 100.0 %
Branches: 73 210 34.8 %

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

Generated by: LCOV version 1.9