s4-smbtorture: Show that the KDC provides no protection from CVE-2017-11103
[samba.git] / source4 / torture / krb5 / kdc-heimdal.c
index 4ea779b90edb6ead5fc4a772774a0a752bd7ec03..aa610d33a9f84d21ba87e89beafd1d869586deef 100644 (file)
@@ -62,6 +62,8 @@ struct torture_krb5_context {
        int packet_count;
        AS_REQ as_req;
        AS_REP as_rep;
+       const char *krb5_service;
+       const char *krb5_hostname;
 };
 
 /*
@@ -72,7 +74,7 @@ struct torture_krb5_context {
  *
  */
 
-static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf)
+static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, krb5_data *send_buf)
 {
        size_t used;
        switch (test_context->test)
@@ -93,8 +95,41 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context
                break;
        case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
        case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
+       {
+               AS_REQ mod_as_req;
+               krb5_error_code k5ret;
+               krb5_data modified_send_buf;
+               torture_assert_int_equal(test_context->tctx,
+                                        decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0,
+                                        "decode_AS_REQ failed");
+               torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
+               torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno");
+
+               /* Only change it if configured with --option=torture:krb5-hostname= */
+               if (test_context->krb5_hostname[0] == '\0') {
+                       break;
+               }
+
+               mod_as_req = test_context->as_req;
+
+               torture_assert_int_equal(test_context->tctx,
+                                        mod_as_req.req_body.sname->name_string.len, 2,
+                                        "Sending wrong mod_as_req.req_body->sname.name_string.len");
+               free(mod_as_req.req_body.sname->name_string.val[0]);
+               free(mod_as_req.req_body.sname->name_string.val[1]);
+               mod_as_req.req_body.sname->name_string.val[0] = strdup(test_context->krb5_service);
+               mod_as_req.req_body.sname->name_string.val[1] = strdup(test_context->krb5_hostname);
+
+               ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf.data, modified_send_buf.length,
+                                  &mod_as_req, &used, k5ret);
+               torture_assert_int_equal(test_context->tctx,
+                                        k5ret, 0,
+                                        "encode_AS_REQ failed");
+
+               *send_buf = modified_send_buf;
                break;
        }
+       }
        return true;
 }
 
@@ -242,14 +277,16 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex
                        torture_assert(test_context->tctx,
                                       test_context->as_rep.ticket.enc_part.kvno,
                                       "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
-                       if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
-                               torture_assert_int_not_equal(test_context->tctx,
-                                                            *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
-                                                            0, "Did not get a RODC number in the KVNO");
-                       } else {
-                               torture_assert_int_equal(test_context->tctx,
-                                                        *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
+                       if (test_context->test == TORTURE_KRB5_TEST_PLAIN) {
+                               if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
+                                       torture_assert_int_not_equal(test_context->tctx,
+                                                                    *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
+                                                                    0, "Did not get a RODC number in the KVNO");
+                               } else {
+                                       torture_assert_int_equal(test_context->tctx,
+                                                                *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
                                                         0, "Unexpecedly got a RODC number in the KVNO");
+                               }
                        }
                        free_AS_REP(&test_context->as_rep);
                }
@@ -527,17 +564,18 @@ static krb5_error_code smb_krb5_send_and_recv_func_override(krb5_context context
 {
        krb5_error_code k5ret;
        bool ok;
+       krb5_data modified_send_buf = *send_buf;
 
        struct torture_krb5_context *test_context
                = talloc_get_type_abort(data, struct torture_krb5_context);
 
-       ok = torture_krb5_pre_send_test(test_context, send_buf);
+       ok = torture_krb5_pre_send_test(test_context, &modified_send_buf);
        if (ok == false) {
                return EINVAL;
        }
 
        k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
-                                                   hi, timeout, send_buf, recv_buf);
+                                                   hi, timeout, &modified_send_buf, recv_buf);
        if (k5ret != 0) {
                return k5ret;
        }
@@ -572,6 +610,9 @@ static bool torture_krb5_init_context(struct torture_context *tctx,
        test_context->test = test;
        test_context->tctx = tctx;
 
+       test_context->krb5_service = torture_setting_string(tctx, "krb5-service", "host");
+       test_context->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
+
        k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context);
        torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
 
@@ -605,6 +646,9 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
        const char *expected_principal_string;
        krb5_get_init_creds_opt *krb_options = NULL;
        const char *realm;
+       const char *krb5_service = torture_setting_string(tctx, "krb5-service", "host");
+       const char *krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
+
 
        ok = torture_krb5_init_context(tctx, test, &smb_krb5_context);
        torture_assert(tctx, ok, "torture_krb5_init_context failed");
@@ -760,9 +804,54 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
 
        case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
        case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
+       {
+               char *got_principal_string;
+               char *assertion_message;
                torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed");
+
+               torture_assert_int_equal(tctx,
+                                        krb5_principal_get_type(k5_context,
+                                                                my_creds.client),
+                                        KRB5_NT_PRINCIPAL,
+                                        "smb_krb5_init_context gave incorrect client->name.name_type");
+
+               torture_assert_int_equal(tctx,
+                                        krb5_unparse_name(k5_context,
+                                                          my_creds.client,
+                                                          &got_principal_string), 0,
+                                        "krb5_unparse_name failed");
+
+               assertion_message = talloc_asprintf(tctx,
+                                                   "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
+                                                   got_principal_string, expected_principal_string);
+               krb5_free_unparsed_name(k5_context, got_principal_string);
+
+               torture_assert(tctx, krb5_principal_compare(k5_context,
+                                                           my_creds.client,
+                                                           principal),
+                              assertion_message);
+
+               if (krb5_hostname[0] == '\0') {
+                       break;
+               }
+
+               torture_assert_str_equal(tctx,
+                                        my_creds.server->name.name_string.val[0],
+                                        krb5_service,
+                                        "Mismatch in name[0] between AS_REP and expected response");
+               torture_assert_str_equal(tctx,
+                                        my_creds.server->name.name_string.val[1],
+                                        krb5_hostname,
+                                        "Mismatch in name[1] between AS_REP and expected response");
+
+               torture_assert_str_equal(tctx,
+                                        my_creds.server->realm,
+                                        realm,
+                                        "Mismatch in server realm in AS_REP, expected krbtgt/REALM@REALM");
+
                break;
        }
+       }
 
        k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds);
        torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed");