third_party/heimdal: Import lorikeet-heimdal-202311082119 (commit 844610f06bac2b7b2a2...
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Thu, 2 Nov 2023 03:34:52 +0000 (16:34 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 9 Nov 2023 08:00:30 +0000 (08:00 +0000)
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
12 files changed:
selftest/knownfail_heimdal_kdc
third_party/heimdal/admin/add.c
third_party/heimdal/lib/hx509/ca.c
third_party/heimdal/lib/hx509/hxtool-commands.in
third_party/heimdal/lib/hx509/hxtool.c
third_party/heimdal/lib/hx509/libhx509-exports.def
third_party/heimdal/lib/hx509/req.c
third_party/heimdal/lib/hx509/test_req.in
third_party/heimdal/lib/hx509/version-script.map
third_party/heimdal/lib/wind/test-utf8.c
third_party/heimdal/lib/wind/utf8.c
third_party/heimdal/tests/kdc/check-bx509.in

index ca11d7ecd8e6a8c7cd39d3f2766601bc4b3e6d47..62eab29cf5c0c242e236ec4c04423b1a5ca93c68 100644 (file)
 ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_not_revealed
 ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_revealed
 #
-# Unicode tests
-#
-^samba\.tests\.krb5\.as_req_tests\.samba\.tests\.krb5\.as_req_tests\.AsReqKerberosTests\.test_as_req_unicode\(fl2008r2dc\)$
-^samba\.tests\.krb5\.as_req_tests\.samba\.tests\.krb5\.as_req_tests\.AsReqKerberosTests\.test_as_req_unicode\(fl2003dc\)$
-^samba\.tests\.krb5\.kdc_tgs_tests\.samba\.tests\.krb5\.kdc_tgs_tests\.KdcTgsTests\.test_fast_as_req_unicode\(ad_dc\)$
-^samba\.tests\.krb5\.kdc_tgs_tests\.samba\.tests\.krb5\.kdc_tgs_tests\.KdcTgsTests\.test_fast_unicode\(ad_dc\)$
-^samba\.tests\.krb5\.kdc_tgs_tests\.samba\.tests\.krb5\.kdc_tgs_tests\.KdcTgsTests\.test_renew_unicode\(ad_dc\)$
-^samba\.tests\.krb5\.kdc_tgs_tests\.samba\.tests\.krb5\.kdc_tgs_tests\.KdcTgsTests\.test_s4u2self_unicode\(ad_dc\)$
-^samba\.tests\.krb5\.kdc_tgs_tests\.samba\.tests\.krb5\.kdc_tgs_tests\.KdcTgsTests\.test_tgs_unicode\(ad_dc\)$
-^samba\.tests\.krb5\.kdc_tgs_tests\.samba\.tests\.krb5\.kdc_tgs_tests\.KdcTgsTests\.test_user2user_unicode\(ad_dc\)$
-^samba\.tests\.krb5\.kdc_tgs_tests\.samba\.tests\.krb5\.kdc_tgs_tests\.KdcTgsTests\.test_validate_unicode\(ad_dc\)$
-#
 # Protected Users tests
 #
 # This test fails, which is fine, as we have an alternate test that considers a policy error as successful.
index 3fba391388189d42ecf945ae4eeca89b275aca25..5f3d584590439a7224eaa4121a82f9dae01f5d6c 100644 (file)
@@ -187,7 +187,7 @@ read_file(FILE *f)
             if ((tmp = realloc(res, alloced + (alloced > 1))) == NULL)
                 err(1, "Out of memory");
             alloced += alloced > 1;
-            p = tmp + (p - res);
+            p = tmp + len;
             res = tmp;
             end = res + alloced;
         }
index 02e256314d7106507bde40d239a4d1a45e036d83..37fb403293ce6f84689f0234accfaba45a994ada 100644 (file)
@@ -449,7 +449,8 @@ hx509_ca_tbs_set_from_csr(hx509_context context,
     int ret;
 
     if (hx509_request_count_unauthorized(req)) {
-        hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+        hx509_set_error_string(context, 0, EACCES,
+                               "Some certificate features requested in the CSR were not authorized");
         return EACCES;
     }
 
index 1bd0119ad7244eba5db4819b72d77dabc21347a6..40df936da2b63ffc24283880aedc9f081f9757a6 100644 (file)
@@ -475,6 +475,22 @@ command = {
 }
 command = {
        name = "request-create"
+       option = {
+               long = "ca"
+               type = "flag"
+               help = "Request CA certificate"
+       }
+       option = {
+               long = "ca-path-length"
+               type = "integer"
+               help = "Path length constraint for CA certificate"
+               default = "-1"
+       }
+       option = {
+               long = "ee"
+               type = "flag"
+               help = "Include BasicConstraints w/ cA set to false"
+       }
        option = {
                long = "subject"
                type = "string"
index f61187163c3f5bd655315201772402fabc15e79c..e4e9a80219521d580492153cee302813b78cdfd9 100644 (file)
@@ -1498,6 +1498,25 @@ request_create(struct request_create_options *opt, int argc, char **argv)
     if (ret)
        hx509_err(context, 1, ret, "Could not initialize CSR context");
 
+    if (opt->ca_flag && opt->ee_flag)
+       errx(1, "request-create --ca and --ee are mutually exclusive");
+
+    if (opt->ca_flag) {
+        unsigned pathLenConstraint = 0;
+        unsigned *pathLenConstraintPtr = NULL;
+
+        if (opt->ca_path_length_integer > 0 &&
+            opt->ca_path_length_integer < INT_MAX) {
+            pathLenConstraint = opt->ca_path_length_integer;
+            pathLenConstraintPtr = &pathLenConstraint;
+        }
+        ret = hx509_request_set_cA(context, req, pathLenConstraintPtr);
+       if (ret)
+           errx(1, "hx509_request_set_cA: %d\n", ret);
+    } else if (opt->ee_flag) {
+        hx509_request_set_eE(context, req);
+    }
+
     if (opt->subject_string) {
        hx509_name name = NULL;
 
index 81783ff7c34cf29bbaac1c056c7bba82990b8296..12c45daf7f654b77864f345f20764a0cdaec2535 100644 (file)
@@ -36,6 +36,7 @@ EXPORTS
        hx509_request_add_pkinit
        hx509_request_add_registered
        hx509_request_add_xmpp_name
+       hx509_request_authorize_cA
        hx509_request_authorize_ku
        hx509_request_authorize_eku
        hx509_request_authorize_san
@@ -48,6 +49,8 @@ EXPORTS
        _hx509_private_key_ref
        hx509_request_eku_authorized_p
        hx509_request_free
+       hx509_request_get_cA
+       hx509_request_get_cA_pathLenConstraint
        hx509_request_get_eku
        hx509_request_get_exts
        hx509_request_get_ku
@@ -63,6 +66,8 @@ EXPORTS
        hx509_request_add_email
        hx509_request_reject_eku
        hx509_request_reject_san
+       hx509_request_set_cA
+       hx509_request_set_eE
        hx509_request_set_name
        hx509_request_set_ku
        hx509_request_san_authorized_p
index d0bfe91a948bea0180ea4f0c57594617ba00ef68..3cbe4a39da4adeb68af29f2db02270f80c6b6956 100644 (file)
@@ -46,11 +46,15 @@ struct hx509_request_data {
     KeyUsage ku;
     ExtKeyUsage eku;
     GeneralNames san;
+    BasicConstraints bc;
     struct abitstring_s authorized_EKUs;
     struct abitstring_s authorized_SANs;
-    uint32_t nunsupported;  /* Count of unsupported features requested */
-    uint32_t nauthorized;   /* Count of supported   features authorized */
+    uint32_t nunsupported_crit; /* Count of unsupported critical features requested */
+    uint32_t nunsupported_opt;  /* Count of unsupported optional features requested */
+    uint32_t nauthorized;       /* Count of supported   features authorized */
+    uint32_t ca_is_authorized:1;
     uint32_t ku_are_authorized:1;
+    uint32_t include_BasicConstraints:1;
 };
 
 /**
@@ -97,10 +101,55 @@ hx509_request_free(hx509_request *reqp)
     free_SubjectPublicKeyInfo(&req->key);
     free_ExtKeyUsage(&req->eku);
     free_GeneralNames(&req->san);
+    free_BasicConstraints(&req->bc);
     memset(req, 0, sizeof(*req));
     free(req);
 }
 
+/**
+ * Make the CSR request a CA certificate
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request to alter.
+ * @param pathLenConstraint the pathLenConstraint for the BasicConstraints (optional)
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_set_cA(hx509_context context,
+                     hx509_request req,
+                     unsigned *pathLenConstraint)
+{
+    req->bc.cA = 1;
+    if (pathLenConstraint) {
+        if (req->bc.pathLenConstraint == NULL)
+            req->bc.pathLenConstraint = malloc(sizeof(*pathLenConstraint));
+        if (req->bc.pathLenConstraint == NULL)
+            return ENOMEM;
+        *req->bc.pathLenConstraint = *pathLenConstraint;
+    }
+    return 0;
+}
+
+/**
+ * Make the CSR request an EE (end-entity, i.e., not a CA) certificate
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request to alter.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_request_set_eE(hx509_context context, hx509_request req)
+{
+    req->bc.cA = 0;
+    free(req->bc.pathLenConstraint);
+    req->include_BasicConstraints = 1;
+    req->ca_is_authorized = 0;
+}
+
 /**
  * Set the subjectName of the CSR.
  *
@@ -524,6 +573,29 @@ get_exts(hx509_context context,
     exts->val = NULL;
     exts->len = 0;
 
+    if (req->bc.cA || req->include_BasicConstraints) {
+        Extension e;
+
+        /*
+         * If `!req->bc.cA' then we don't include BasicConstraints unless
+         * hx509_request_set_eE() was called on this request, and in that case
+         * we make this extension non-critical.  We do this to emulate Dell
+         * iDRAC CSR-making software.
+         *
+         * If `req->bc.cA' then we make the BasicConstraints critical,
+         * obviously.
+         */
+        memset(&e, 0, sizeof(e));
+        e.critical = req->bc.cA ? 1 : 0;
+        if (ret == 0)
+            ASN1_MALLOC_ENCODE(BasicConstraints, e.extnValue.data, e.extnValue.length,
+                               &req->bc, &size, ret);
+        if (ret == 0)
+            ret = der_copy_oid(&asn1_oid_id_x509_ce_basicConstraints, &e.extnID);
+        if (ret == 0)
+            ret = add_Extensions(exts, &e);
+        free_Extension(&e);
+    }
     if (KeyUsage2int(req->ku)) {
         Extension e;
 
@@ -851,8 +923,12 @@ hx509_request_parse_der(hx509_context context,
              * Count all KUs as one requested extension to be authorized,
              * though the caller will have to check the KU values individually.
              */
-            if (KeyUsage2int((*req)->ku) & ~KeyUsage2int(int2KeyUsage(~0)))
-                (*req)->nunsupported++;
+            if (KeyUsage2int((*req)->ku) & ~KeyUsage2int(int2KeyUsage(~0ULL))) {
+                if (e->critical)
+                    (*req)->nunsupported_crit++;
+                else
+                    (*req)->nunsupported_opt++;
+            }
         } else if (der_heim_oid_cmp(&e->extnID,
                                     &asn1_oid_id_x509_ce_extKeyUsage) == 0) {
             ret = decode_ExtKeyUsage(e->extnValue.data, e->extnValue.length,
@@ -873,10 +949,18 @@ hx509_request_parse_der(hx509_context context,
              * Count each SAN as a separate requested extension to be
              * authorized.
              */
+        } else if (der_heim_oid_cmp(&e->extnID,
+                                    &asn1_oid_id_x509_ce_basicConstraints) == 0) {
+            (*req)->include_BasicConstraints = 1;
+            ret = decode_BasicConstraints(e->extnValue.data, e->extnValue.length,
+                                          &(*req)->bc, NULL);
         } else {
             char *oidstr = NULL;
 
-            (*req)->nunsupported++;
+            if (e->critical)
+                (*req)->nunsupported_crit++;
+            else
+                (*req)->nunsupported_opt++;
 
             /*
              * We need an HX509_TRACE facility for this sort of warning.
@@ -1073,6 +1157,26 @@ reject_feat(hx509_request req, abitstring a, size_t n, int idx)
     }
 }
 
+/**
+ * Authorize issuance of a CA certificate as requested.
+ *
+ * @param req The hx509_request object.
+ * @param pathLenConstraint the pathLenConstraint for the BasicConstraints (optional)
+ *
+ * @return an hx509 or system error.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_authorize_cA(hx509_request req, unsigned *pathLenConstraint)
+{
+    int ret;
+
+    ret = hx509_request_set_cA(NULL, req, pathLenConstraint);
+    req->ca_is_authorized++;
+    return ret;
+}
+
 /**
  * Filter the requested KeyUsage and mark it authorized.
  *
@@ -1201,7 +1305,7 @@ hx509_request_san_authorized_p(hx509_request req, size_t idx)
 HX509_LIB_FUNCTION size_t HX509_LIB_CALL
 hx509_request_count_unsupported(hx509_request req)
 {
-    return req->nunsupported;
+    return req->nunsupported_crit;
 }
 
 /**
@@ -1216,9 +1320,10 @@ HX509_LIB_FUNCTION size_t HX509_LIB_CALL
 hx509_request_count_unauthorized(hx509_request req)
 {
     size_t nrequested = req->eku.len + req->san.len +
-        (KeyUsage2int(req->ku) ? 1 : 0) + req->nunsupported;
+        (KeyUsage2int(req->ku) ? 1 : 0) + !!req->bc.cA +
+        req->nunsupported_crit;
 
-    return nrequested - (req->nauthorized + req->ku_are_authorized);
+    return nrequested - (req->nauthorized + req->ku_are_authorized + req->ca_is_authorized);
 }
 
 static hx509_san_type
@@ -1390,6 +1495,45 @@ hx509_request_get_san(hx509_request req,
     return 0;
 }
 
+/**
+ * Indicate if a CSR requested a CA certificate.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ *
+ * @return 1 if the CSR requested CA certificate, 0 otherwise.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_get_cA(hx509_context context,
+                     hx509_request req)
+{
+    return req->bc.cA;
+}
+
+/**
+ * Return the CSR's requested BasicConstraints pathLenConstraint.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ *
+ * @return -1 if no pathLenConstraint was requested (or the BasicConstraints
+ * does not request a CA certificate), or the actual requested
+ * pathLenConstraint.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_get_cA_pathLenConstraint(hx509_context context,
+                                       hx509_request req)
+{
+    if (req->bc.cA && req->bc.pathLenConstraint &&
+        *req->bc.pathLenConstraint < INT_MAX)
+        return *req->bc.pathLenConstraint;
+    return -1;
+}
+
 /**
  * Display a CSR.
  *
@@ -1437,6 +1581,13 @@ hx509_request_print(hx509_context context, hx509_request req, FILE *f)
      */
     fprintf(f, "PKCS#10 CertificationRequest:\n");
 
+    if (req->include_BasicConstraints) {
+        fprintf(f, "  cA: %s\n", req->bc.cA ? "yes" : "no");
+        if (req->bc.pathLenConstraint)
+            fprintf(f, "  pathLenConstraint: %u\n", *req->bc.pathLenConstraint);
+        else
+            fprintf(f, "  pathLenConstraint: unspecified\n");
+    }
     if (req->name) {
        char *subject;
        ret = hx509_name_to_string(req->name, &subject);
@@ -1515,6 +1666,14 @@ hx509_request_print(hx509_context context, hx509_request req, FILE *f)
             break;
         }
     }
+    if (req->nunsupported_crit) {
+        fprintf(f, "  unsupported_critical_extensions_count: %u\n",
+                (unsigned)req->nunsupported_crit);
+    }
+    if (req->nunsupported_crit) {
+        fprintf(f, "  unsupported_optional_extensions_count: %u\n",
+                (unsigned)req->nunsupported_opt);
+    }
     free(s); s = NULL;
     if (ret == HX509_NO_ITEM)
         ret = 0;
index 9288df6738f398901768df77cb9b62243e469113..1d553dbafa01b97f632587eef8474574b03cc386 100644 (file)
@@ -161,3 +161,51 @@ Certificate Request:
 EOF
     fi
 fi
+
+${hxtool} request-create \
+         --ca \
+         --ca-path-length=3 \
+         --subject="cn=ca-cert" \
+         --key=FILE:$srcdir/data/key.der \
+         pkcs10-request.der || exit 1
+${hxtool} request-print PKCS10:pkcs10-request.der > "${objdir}/actual"|| exit 1
+cat > "$objdir/expected" <<EOF
+request print
+PKCS#10 CertificationRequest:
+  cA: yes
+  pathLenConstraint: 3
+  name: CN=ca-cert
+EOF
+diff "$objdir/expected" "${objdir}/actual" || exit 1
+
+${hxtool} request-create \
+         --ca \
+         --subject="cn=ca-cert" \
+         --key=FILE:$srcdir/data/key.der \
+         pkcs10-request.der || exit 1
+${hxtool} request-print PKCS10:pkcs10-request.der > "${objdir}/actual"|| exit 1
+cat > "$objdir/expected" <<EOF
+request print
+PKCS#10 CertificationRequest:
+  cA: yes
+  pathLenConstraint: unspecified
+  name: CN=ca-cert
+EOF
+diff "$objdir/expected" "${objdir}/actual" || exit 1
+
+${hxtool} request-create \
+         --ee \
+         --subject="cn=ca-cert" \
+         --key=FILE:$srcdir/data/key.der \
+         pkcs10-request.der || exit 1
+${hxtool} request-print PKCS10:pkcs10-request.der > "${objdir}/actual" || exit 1
+cat > "$objdir/expected" <<EOF
+request print
+PKCS#10 CertificationRequest:
+  cA: no
+  pathLenConstraint: unspecified
+  name: CN=ca-cert
+EOF
+diff "$objdir/expected" "${objdir}/actual" || exit 1
+
+exit 0
index 8f46b0ac051addd99fa8d4cf150c5d69ad033618..27f5f51f76776929ebc649bb2b195e6957ed7955 100644 (file)
@@ -36,6 +36,7 @@ HEIMDAL_X509_1.2 {
                hx509_request_add_pkinit;
                hx509_request_add_registered;
                hx509_request_add_xmpp_name;
+               hx509_request_authorize_cA;
                hx509_request_authorize_ku;
                hx509_request_authorize_eku;
                hx509_request_authorize_san;
@@ -244,6 +245,8 @@ HEIMDAL_X509_1.2 {
                hx509_query_match_option;
                hx509_query_statistic_file;
                hx509_query_unparse_stats;
+               hx509_request_get_cA;
+               hx509_request_get_cA_pathLenConstraint;
                hx509_request_get_eku;
                hx509_request_get_exts;
                hx509_request_get_ku;
@@ -254,6 +257,8 @@ HEIMDAL_X509_1.2 {
                hx509_request_init;
                hx509_request_parse;
                hx509_request_parse_der;
+               hx509_request_set_cA;
+               hx509_request_set_eE;
                hx509_request_set_ku;
                hx509_request_set_name;
                hx509_request_set_SubjectPublicKeyInfo;
index 0b95032ffed9419a84c5bbc7cebc17a71041a795..b70cd74f6c45fde3827871408381a085e7d5503e 100644 (file)
@@ -50,21 +50,32 @@ static const char *failing_testcases[] = {
     "\xF7",
     "\xC0\x01",
     "\xC0\x7F",
+    "\xC0\x80",
+    "\xC0\x81",
     "\xC0\xFF",
     "\xC0\x80\x80",
+    "\xC1\x80",
     "\xE0\x01",
     "\xE0\x7F",
     "\xE0\x80",
     "\xE0\xFF",
     "\xE0\x80\x20",
     "\xE0\x80\xFF",
+    "\xE0\x80\x80",
+    "\xE0\x80\x81",
     "\xE0\x80\x80\x80",
+    "\xE0\x81\x80",
     "\xF0\x01",
     "\xF0\x80",
     "\xF0\x80\x01",
     "\xF0\x80\x80",
     "\xF0\x80\x80\x01",
+    "\xF0\x80\x80\x80",
+    "\xF0\x80\x80\x81",
     "\xF0\x80\x80\xFF",
+    "\xF0\x80\x81\x80",
+    "\xF0\x81\x80\x80",
+    "\xF7\xBF\xBF\xBF",
     NULL
 };
 
@@ -82,21 +93,10 @@ static const struct testcase testcases[] = {
     {"\x01", 1, {1}, 0},
     {"\x7F", 1, {0x7F}, 0},
     {"\x01\x7F", 2, {0x01, 0x7F}, 0},
-    {"\xC0\x80", 1, {0}, 0},
-    {"\xC0\x81", 1, {1}, 0},
-    {"\xC1\x80", 1, {0x40}, 0},
     {"\xDF\xBF", 1, {0x7FF}, 0},
-    {"\xE0\x80\x80", 1, {0}, 0},
-    {"\xE0\x80\x81", 1, {1}, 0},
-    {"\xE0\x81\x80", 1, {0x40}, 0},
     {"\xE1\x80\x80", 1, {0x1000}, 0},
     {"\xEF\xBF\xBF", 1, {0xFFFF}, 0},
-    {"\xF0\x80\x80\x80", 1, {0}, 0},
-    {"\xF0\x80\x80\x81", 1, {1}, 0},
-    {"\xF0\x80\x81\x80", 1, {0x40}, 0},
-    {"\xF0\x81\x80\x80", 1, {0x1000}, 0},
     {"\xF1\x80\x80\x80", 1, {0x40000}, 0},
-    {"\xF7\xBF\xBF\xBF", 1, {0X1FFFFF}, 1},
 };
 
 int
index 4559109f3fd6aee1c65e8423795409ab15478d56..7e71448285d88602b1e05a617d814daf87539297 100644 (file)
@@ -38,25 +38,32 @@ static int
 utf8toutf32(const unsigned char **pp, uint32_t *out)
 {
     const unsigned char *p = *pp;
-    unsigned c = *p;
+    uint32_t c = *p;
+    uint32_t out_val;
 
     if (c & 0x80) {
        if ((c & 0xE0) == 0xC0) {
-           const unsigned c2 = *++p;
+           const uint32_t c2 = *++p;
            if ((c2 & 0xC0) == 0x80) {
-               *out =  ((c  & 0x1F) << 6)
+               out_val =  ((c  & 0x1F) << 6)
                    | (c2 & 0x3F);
+               if (out_val < 0x80) {
+                   return WIND_ERR_INVALID_UTF8;
+               }
            } else {
                return WIND_ERR_INVALID_UTF8;
            }
        } else if ((c & 0xF0) == 0xE0) {
-           const unsigned c2 = *++p;
+           const uint32_t c2 = *++p;
            if ((c2 & 0xC0) == 0x80) {
-               const unsigned c3 = *++p;
+               const uint32_t c3 = *++p;
                if ((c3 & 0xC0) == 0x80) {
-                   *out =   ((c  & 0x0F) << 12)
+                   out_val =   ((c  & 0x0F) << 12)
                        | ((c2 & 0x3F) << 6)
                        |  (c3 & 0x3F);
+                   if (out_val < 0x800) {
+                       return WIND_ERR_INVALID_UTF8;
+                   }
                } else {
                    return WIND_ERR_INVALID_UTF8;
                }
@@ -64,16 +71,19 @@ utf8toutf32(const unsigned char **pp, uint32_t *out)
                return WIND_ERR_INVALID_UTF8;
            }
        } else if ((c & 0xF8) == 0xF0) {
-           const unsigned c2 = *++p;
+           const uint32_t c2 = *++p;
            if ((c2 & 0xC0) == 0x80) {
-               const unsigned c3 = *++p;
+               const uint32_t c3 = *++p;
                if ((c3 & 0xC0) == 0x80) {
-                   const unsigned c4 = *++p;
+                   const uint32_t c4 = *++p;
                    if ((c4 & 0xC0) == 0x80) {
-                       *out =   ((c  & 0x07) << 18)
+                       out_val =   ((c  & 0x07) << 18)
                            | ((c2 & 0x3F) << 12)
                            | ((c3 & 0x3F) <<  6)
                            |  (c4 & 0x3F);
+                       if (out_val < 0x10000) {
+                           return WIND_ERR_INVALID_UTF8;
+                       }
                    } else {
                        return WIND_ERR_INVALID_UTF8;
                    }
@@ -87,9 +97,16 @@ utf8toutf32(const unsigned char **pp, uint32_t *out)
            return WIND_ERR_INVALID_UTF8;
        }
     } else {
-       *out = c;
+       out_val = c;
     }
 
+    /* Allow unpaired surrogates (in the range 0xd800–0xdfff). */
+
+    if (out_val > 0x10ffff) {
+       return WIND_ERR_INVALID_UTF8;
+    }
+
+    *out = out_val;
     *pp = p;
 
     return 0;
@@ -220,8 +237,8 @@ wind_ucs4utf8(const uint32_t *in, size_t in_len, char *out, size_t *out_len)
             default:
                 break;
            }
+           out += len;
        }
-       out += len;
     }
     if (out) {
        if (o + 1 >= *out_len)
@@ -412,15 +429,30 @@ wind_utf8ucs2(const char *in, uint16_t *out, size_t *out_len)
        if (ret)
            return ret;
 
-       if (u & 0xffff0000)
-           return WIND_ERR_NOT_UTF16;
+       if (u >= 0x10000) {
+           if (out) {
+               uint16_t high_ten_bits;
+               uint16_t low_ten_bits;
 
-       if (out) {
-           if (o >= *out_len)
-               return WIND_ERR_OVERRUN;
-           out[o] = u;
+               if (o + 2 > *out_len)
+                   return WIND_ERR_OVERRUN;
+
+               u -= 0x10000;
+               high_ten_bits = (u >> 10) & 0x3ff;
+               low_ten_bits = u & 0x3ff;
+
+               out[o] = 0xd800 | high_ten_bits;
+               out[o+1] = 0xdc00 | low_ten_bits;
+           }
+           o += 2;
+       } else {
+           if (out) {
+               if (o >= *out_len)
+                   return WIND_ERR_OVERRUN;
+               out[o] = u;
+           }
+           o++;
        }
-       o++;
     }
     *out_len = o;
     return 0;
@@ -431,7 +463,7 @@ wind_utf8ucs2(const char *in, uint16_t *out, size_t *out_len)
  * string.
  *
  * @param in an UTF-8 string to convert.
- * @param out_len the length of the resulting UCS4 string.
+ * @param out_len the length of the resulting UCS2 string.
  *
  * @return returns 0 on success, an wind error code otherwise
  * @ingroup wind
@@ -463,7 +495,7 @@ wind_utf8ucs2_length(const char *in, size_t *out_len)
 int
 wind_ucs2utf8(const uint16_t *in, size_t in_len, char *out, size_t *out_len)
 {
-    uint16_t ch;
+    uint32_t ch;
     size_t i, len, o;
 
     for (o = 0, i = 0; i < in_len; i++) {
@@ -473,8 +505,36 @@ wind_ucs2utf8(const uint16_t *in, size_t in_len, char *out, size_t *out_len)
            len = 1;
        } else if (ch < 0x800) {
            len = 2;
-       } else
+       } else if (ch < 0xd800 || ch >= 0xe000) {
            len = 3;
+       } else if (ch < 0xdc00) {
+           /* A high surrogate. */
+           if (i < in_len - 1) {
+               uint16_t ch2 = in[i + 1];
+
+               if (ch2 >= 0xdc00 && ch2 < 0xe000) {
+                   uint16_t high_ten_bits;
+                   uint16_t low_ten_bits;
+
+                   /* A surrogate pair. */
+                   high_ten_bits = ch & 0x3ff;
+                   low_ten_bits = ch2 & 0x3ff;
+
+                   ch = 0x10000 + ((uint32_t)high_ten_bits << 10 | low_ten_bits);
+                   len = 4;
+                   ++i;
+               } else {
+                   /* An unpaired high surrogate. */
+                   len = 3;
+               }
+           } else {
+               /* An unpaired high surrogate. */
+               len = 3;
+           }
+       } else {
+           /* An unpaired low surrogate. */
+           len = 3;
+       }
 
        o += len;
 
@@ -483,6 +543,10 @@ wind_ucs2utf8(const uint16_t *in, size_t in_len, char *out, size_t *out_len)
                return WIND_ERR_OVERRUN;
 
            switch(len) {
+           case 4:
+               out[3] = (ch | 0x80) & 0xbf;
+               ch = ch >> 6;
+               HEIM_FALLTHROUGH;
            case 3:
                out[2] = (ch | 0x80) & 0xbf;
                ch = ch >> 6;
index d1e63741e1a8b36f37d4794820f547e020671a44..6d894effb1d5f01ce24b7548d905d4ebf808403d 100644 (file)
@@ -438,15 +438,61 @@ ec=0
 
 rm -f trivial.pem server.pem email.pem
 
-echo "Making a plain CSR"
+echo "Making a plain CSR (with BasicConstraints requesting CA cert)"
 csr_revoke
-$hxtool request-create  --subject='' --generate-key=rsa --key-bits=1024 \
-                        --key=FILE:"${objdir}/k.der" "${objdir}/req" ||
+$hxtool request-create  --subject='CN=H5LCA' --generate-key=rsa --key-bits=1024 \
+                        --ca --key=FILE:"${objdir}/k.der" "${objdir}/req" ||
     { echo "Failed to make a CSR"; exit 2; }
 
 # XXX Add autoconf check for curl?
 #     Create a barebones bx509 HTTP/1.1 client test program?
 
+echo "Fail to get a certificate using a CSR requesting a CA cert"
+# Encode the CSR in base64, then URL-encode it
+csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
+token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
+if (set -vx; get_cert '' -sf -o "${objdir}/trivial.pem"); then
+    echo 'Issued a certificate for a CSR that requested a CA cert!'
+    exit 1
+else
+    echo 'CSRs requesting CA certs properly rejected'
+fi
+
+echo "Making a plain CSR (with BasicConstraints requesting EE cert)"
+csr_revoke
+$hxtool request-create  --subject='' --generate-key=rsa --key-bits=1024 \
+                        --ee --key=FILE:"${objdir}/k.der" "${objdir}/req" ||
+    { echo "Failed to make a CSR"; exit 2; }
+
+echo "Fetching a trivial user certificate"
+# Encode the CSR in base64, then URL-encode it
+csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
+token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
+if (set -vx; get_cert '' -sf -o "${objdir}/trivial.pem"); then
+    $hxtool print --content "FILE:${objdir}/trivial.pem"
+    if $hxtool acert --end-entity                                            \
+                    --expr="%{certificate.subject} == \"CN=foo,$DCs\""  \
+                    -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
+        echo 'Successfully obtained a trivial client certificate with a CSR w/ BasicConstraints requesting EE cert!'
+    else
+        echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
+        exit 1
+    fi
+    if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\""   \
+                     --has-private-key "FILE:${objdir}/trivial.pem"; then
+        echo 'Successfully obtained a trivial client certificate!'
+    fi
+else
+    echo 'Failed to get a certificate with a CSR w/ BasicConstraints requesting EE cert!'
+    exit 1
+fi
+
+echo "Making a plain CSR"
+csr_revoke
+$hxtool request-create  --subject='' --generate-key=rsa --key-bits=1024 \
+                        --key=FILE:"${objdir}/k.der" "${objdir}/req" ||
+    { echo "Failed to make a CSR"; exit 2; }
+
 echo "Fetching a trivial user certificate (no authentication, must fail)"
 # Encode the CSR in base64, then URL-encode it
 csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)