s4-smbtorture: Show that the KDC provides no protection from CVE-2017-11103
[samba.git] / source4 / torture / krb5 / kdc-heimdal.c
index 52014f7570601fcf970b19fc41d5a7a33d0e1bab..aa610d33a9f84d21ba87e89beafd1d869586deef 100644 (file)
 #include "source4/auth/kerberos/kerberos_util.h"
 #include "lib/util/util_net.h"
 
+#define krb5_is_app_tag(dat,tag)                          \
+       ((dat != NULL) && (dat)->length &&                \
+        (((((char *)(dat)->data)[0] & ~0x20) == ((tag) | 0x40))))
+
+#define krb5_is_krb_error(dat)                krb5_is_app_tag(dat, 30)
+
 enum torture_krb5_test {
        TORTURE_KRB5_TEST_PLAIN,
        TORTURE_KRB5_TEST_PAC_REQUEST,
        TORTURE_KRB5_TEST_BREAK_PW,
        TORTURE_KRB5_TEST_CLOCK_SKEW,
+       TORTURE_KRB5_TEST_AES,
+       TORTURE_KRB5_TEST_RC4,
+       TORTURE_KRB5_TEST_AES_RC4,
+
+       /* 
+        * This is in and out of the client. 
+        * Out refers to requests, in refers to replies
+        */
+       TORTURE_KRB5_TEST_CHANGE_SERVER_OUT,
+       TORTURE_KRB5_TEST_CHANGE_SERVER_IN,
+       TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH,
 };
 
 struct torture_krb5_context {
@@ -45,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;
 };
 
 /*
@@ -55,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)
@@ -64,13 +83,150 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context
        case TORTURE_KRB5_TEST_PAC_REQUEST:
        case TORTURE_KRB5_TEST_BREAK_PW:
        case TORTURE_KRB5_TEST_CLOCK_SKEW:
+       case TORTURE_KRB5_TEST_AES:
+       case TORTURE_KRB5_TEST_RC4:
+       case TORTURE_KRB5_TEST_AES_RC4:
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
+               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");
+               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;
+}
+
+static bool torture_check_krb5_error(struct torture_krb5_context *test_context,
+                                    const krb5_data *reply,
+                                    krb5_error_code expected_error,
+                                    bool check_pa_data)
+{
+       KRB_ERROR error = { 0 };
+       size_t used = 0;
+       int rc;
+
+       rc = decode_KRB_ERROR(reply->data, reply->length, &error, &used);
+       torture_assert_int_equal(test_context->tctx,
+                                rc, 0,
+                                "decode_AS_REP failed");
+
+       torture_assert_int_equal(test_context->tctx,
+                                used, reply->length,
+                                "length mismatch");
+       torture_assert_int_equal(test_context->tctx,
+                                error.pvno, 5,
+                                "Got wrong error.pvno");
+       torture_assert_int_equal(test_context->tctx,
+                                error.error_code, expected_error - KRB5KDC_ERR_NONE,
+                                "Got wrong error.error_code");
+
+       if (check_pa_data) {
+               METHOD_DATA m;
+               size_t len;
+               int i;
+               bool found = false;
+                       torture_assert(test_context->tctx,
+                                      error.e_data != NULL,
+                                      "No e-data returned");
+
+                       rc = decode_METHOD_DATA(error.e_data->data,
+                                               error.e_data->length,
+                                               &m,
+                                               &len);
+                       torture_assert_int_equal(test_context->tctx,
+                                                rc, 0,
+                                                "Got invalid method data");
+
+                       torture_assert(test_context->tctx,
+                                      m.len > 0,
+                                      "No PA_DATA given");
+                       for (i = 0; i < m.len; i++) {
+                               if (m.val[i].padata_type == KRB5_PADATA_ENC_TIMESTAMP) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       torture_assert(test_context->tctx,
+                                      found,
+                                      "Encrypted timestamp not found");
+       }
+
+       free_KRB_ERROR(&error);
+
+       return true;
+}
+
+static bool torture_check_krb5_as_rep_enctype(struct torture_krb5_context *test_context,
+                                             const krb5_data *reply,
+                                             krb5_enctype expected_enctype)
+{
+       ENCTYPE reply_enctype = { 0 };
+       size_t used = 0;
+       int rc;
+
+       rc = decode_AS_REP(reply->data,
+                          reply->length,
+                          &test_context->as_rep,
+                          &used);
+       torture_assert_int_equal(test_context->tctx,
+                                rc, 0,
+                                "decode_AS_REP failed");
+       torture_assert_int_equal(test_context->tctx,
+                                used, reply->length,
+                                "length mismatch");
+       torture_assert_int_equal(test_context->tctx,
+                                test_context->as_rep.pvno, 5,
+                                "Got wrong as_rep->pvno");
+       torture_assert_int_equal(test_context->tctx,
+                                test_context->as_rep.ticket.tkt_vno, 5,
+                                "Got wrong as_rep->ticket.tkt_vno");
+       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");
+
+       reply_enctype = test_context->as_rep.enc_part.etype;
+
+       torture_assert_int_equal(test_context->tctx,
+                                reply_enctype, expected_enctype,
+                                "Ticket encrypted with invalid algorithm");
+
        return true;
 }
 
@@ -82,22 +238,24 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context
  *
  */
 
-static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
+static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, krb5_data *recv_buf)
 {
        KRB_ERROR error;
        size_t used;
+       bool ok;
+
        switch (test_context->test)
        {
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
        case TORTURE_KRB5_TEST_PLAIN:
                if (test_context->packet_count == 0) {
-                       torture_assert_int_equal(test_context->tctx,
-                                                decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
-                                                "decode_AS_REP failed");
-                       torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
-                       torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
-                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
-                                                "Got wrong error.error_code");
-                       free_KRB_ERROR(&error);
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
                } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
                           && (test_context->packet_count == 1)) {
                        torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
@@ -119,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);
                }
@@ -139,23 +299,21 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex
                 */
        case TORTURE_KRB5_TEST_PAC_REQUEST:
                if (test_context->packet_count == 0) {
-                       torture_assert_int_equal(test_context->tctx,
-                                                decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
-                                                "decode_AS_REP failed");
-                       torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
-                       torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
-                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
-                                                "Got wrong error.error_code");
-                       free_KRB_ERROR(&error);
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KRB_ERR_RESPONSE_TOO_BIG,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
                } else if (test_context->packet_count == 1) {
-                       torture_assert_int_equal(test_context->tctx,
-                                                decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
-                                                "decode_AS_REP failed");
-                       torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
-                       torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
-                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
-                                                "Got wrong error.error_code");
-                       free_KRB_ERROR(&error);
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
                } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
                           && (test_context->packet_count == 2)) {
                        torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
@@ -180,41 +338,21 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex
                 */
        case TORTURE_KRB5_TEST_BREAK_PW:
                if (test_context->packet_count == 0) {
-                       torture_assert_int_equal(test_context->tctx,
-                                                decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
-                                                "decode_AS_REP failed");
-                       torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
-                       torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
-                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
-                                                "Got wrong error.error_code");
-                       free_KRB_ERROR(&error);
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
                } else if (test_context->packet_count == 1) {
-                       METHOD_DATA m;
-                       size_t len;
-                       int i, ret = 0;
-                       bool found = false;
-                       torture_assert_int_equal(test_context->tctx,
-                                                decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
-                                                "decode_AS_REP failed");
-                       torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
-                       torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
-                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_FAILED - KRB5KDC_ERR_NONE,
-                                                "Got wrong error.error_code");
-                       torture_assert(test_context->tctx, error.e_data != NULL, "No e-data returned");
-                       ret = decode_METHOD_DATA(error.e_data->data, error.e_data->length, &m, &len);
-                       torture_assert_int_equal(test_context->tctx, ret, 0,
-                                                "Got invalid method data");
-
-                       torture_assert(test_context->tctx, m.len > 0, "No PA_DATA given");
-                       for (i = 0; i < m.len; i++) {
-                               if (m.val[i].padata_type == KRB5_PADATA_ENC_TIMESTAMP) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-                       torture_assert(test_context->tctx, found, "Encrypted timestamp not found");
-
-                       free_KRB_ERROR(&error);
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_FAILED,
+                                                     true);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
                }
                torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
                free_AS_REQ(&test_context->as_req);
@@ -225,28 +363,180 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex
                 */
        case TORTURE_KRB5_TEST_CLOCK_SKEW:
                if (test_context->packet_count == 0) {
-                       torture_assert_int_equal(test_context->tctx,
-                                                decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
-                                                "decode_AS_REP failed");
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else if (test_context->packet_count == 1) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KRB_AP_ERR_SKEW,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               }
+               torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
+               free_AS_REQ(&test_context->as_req);
+               break;
+       case TORTURE_KRB5_TEST_AES:
+               torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES\n");
+
+               if (test_context->packet_count == 0) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else if (krb5_is_krb_error(recv_buf)) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KRB_ERR_RESPONSE_TOO_BIG,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else {
+                       ok = torture_check_krb5_as_rep_enctype(test_context,
+                                                              recv_buf,
+                                                              KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_as_rep_enctype failed");
+               }
+
+               torture_assert(test_context->tctx,
+                              test_context->packet_count < 3,
+                              "Too many packets");
+               break;
+       case TORTURE_KRB5_TEST_RC4:
+               torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_RC4\n");
+
+               if (test_context->packet_count == 0) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else if (krb5_is_krb_error(recv_buf)) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KRB_ERR_RESPONSE_TOO_BIG,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else {
+                       ok = torture_check_krb5_as_rep_enctype(test_context,
+                                                              recv_buf,
+                                                              KRB5_ENCTYPE_ARCFOUR_HMAC_MD5);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_as_rep_enctype failed");
+               }
+
+               torture_assert(test_context->tctx,
+                              test_context->packet_count < 3,
+                              "Too many packets");
+               break;
+       case TORTURE_KRB5_TEST_AES_RC4:
+               torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES_RC4\n");
+
+               if (test_context->packet_count == 0) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else if (krb5_is_krb_error(recv_buf)) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KRB_ERR_RESPONSE_TOO_BIG,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else {
+                       ok = torture_check_krb5_as_rep_enctype(test_context,
+                                                              recv_buf,
+                                                              KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_as_rep_enctype failed");
+               }
+
+               torture_assert(test_context->tctx,
+                              test_context->packet_count < 3,
+                              "Too many packets");
+               break;
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
+       {
+               AS_REP mod_as_rep;
+               krb5_error_code k5ret;
+               krb5_data modified_recv_buf;
+               if (test_context->packet_count == 0) {
+                       ok = torture_check_krb5_error(test_context,
+                                                     recv_buf,
+                                                     KRB5KDC_ERR_PREAUTH_REQUIRED,
+                                                     false);
+                       torture_assert(test_context->tctx,
+                                      ok,
+                                      "torture_check_krb5_error failed");
+               } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
+                          && (test_context->packet_count == 1)) {
                        torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
                        torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
-                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
+                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
                                                 "Got wrong error.error_code");
                        free_KRB_ERROR(&error);
-               } else if (test_context->packet_count == 1) {
+               } else {
                        torture_assert_int_equal(test_context->tctx,
-                                                decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
+                                                decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
                                                 "decode_AS_REP failed");
                        torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
-                       torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
-                       torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_AP_ERR_SKEW - KRB5KDC_ERR_NONE,
-                                                "Got wrong error.error_code");
-                       free_KRB_ERROR(&error);
+                       torture_assert_int_equal(test_context->tctx,
+                                                test_context->as_rep.pvno, 5,
+                                                "Got wrong as_rep->pvno");
+                       torture_assert_int_equal(test_context->tctx,
+                                                test_context->as_rep.ticket.tkt_vno, 5,
+                                                "Got wrong as_rep->ticket.tkt_vno");
+                       torture_assert_int_equal(test_context->tctx,
+                                                test_context->as_rep.ticket.sname.name_string.len, 2,
+                                                "Got wrong as_rep->ticket.sname.name_string.len");
+                       free(test_context->as_rep.ticket.sname.name_string.val[0]);
+                       free(test_context->as_rep.ticket.sname.name_string.val[1]);
+                       test_context->as_rep.ticket.sname.name_string.val[0] = strdup("bad");
+                       test_context->as_rep.ticket.sname.name_string.val[1] = strdup("mallory");
+
+                       mod_as_rep = test_context->as_rep;
+
+                       ASN1_MALLOC_ENCODE(AS_REP, modified_recv_buf.data, modified_recv_buf.length,
+                                          &mod_as_rep, &used, k5ret);
+                       torture_assert_int_equal(test_context->tctx,
+                                                k5ret, 0,
+                                                "encode_AS_REQ failed");
+                       krb5_data_free(recv_buf);
+
+                       *recv_buf = modified_recv_buf;
+                       free_AS_REQ(&test_context->as_req);
                }
-               torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
-               free_AS_REQ(&test_context->as_req);
+               torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
+
                break;
        }
+       }
+
+
        return true;
 }
 
@@ -274,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;
        }
@@ -319,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");
 
@@ -345,14 +639,26 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
        krb5_creds my_creds;
        krb5_principal principal;
        struct smb_krb5_context *smb_krb5_context;
+       krb5_context k5_context;
        enum credentials_obtained obtained;
        const char *error_string;
        const char *password = cli_credentials_get_password(credentials);
+       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");
+       k5_context = smb_krb5_context->krb5_context;
+
+       expected_principal_string
+               = cli_credentials_get_principal(credentials,
+                                               tctx);
 
+       realm = strupper_talloc(tctx, cli_credentials_get_realm(credentials));
        k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context,
                                           &principal, &obtained,  &error_string);
        torture_assert_int_equal(tctx, k5ret, 0, error_string);
@@ -360,6 +666,9 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
        switch (test)
        {
        case TORTURE_KRB5_TEST_PLAIN:
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
                break;
 
        case TORTURE_KRB5_TEST_PAC_REQUEST:
@@ -382,8 +691,52 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
                                         0, "krb5_set_real_time failed");
                break;
 
-       break;
+       case TORTURE_KRB5_TEST_AES: {
+               krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 };
+
+               k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
+                                                     &krb_options);
+               torture_assert_int_equal(tctx,
+                                        k5ret, 0,
+                                        "krb5_get_init_creds_opt_alloc failed");
+
+               krb5_get_init_creds_opt_set_etype_list(krb_options,
+                                                      etype_list,
+                                                      1);
+               break;
        }
+       case TORTURE_KRB5_TEST_RC4: {
+               krb5_enctype etype_list[] = { KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
+
+               k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
+                                                     &krb_options);
+               torture_assert_int_equal(tctx,
+                                        k5ret, 0,
+                                        "krb5_get_init_creds_opt_alloc failed");
+
+               krb5_get_init_creds_opt_set_etype_list(krb_options,
+                                                      etype_list,
+                                                      1);
+               break;
+       }
+       case TORTURE_KRB5_TEST_AES_RC4: {
+               krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+                                             KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
+
+               k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
+                                                     &krb_options);
+               torture_assert_int_equal(tctx,
+                                        k5ret, 0,
+                                        "krb5_get_init_creds_opt_alloc failed");
+
+               krb5_get_init_creds_opt_set_etype_list(krb_options,
+                                                      etype_list,
+                                                      2);
+               break;
+       }
+
+       } /* end switch */
+
        k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal,
                                             password, NULL, NULL, 0,
                                             NULL, krb_options);
@@ -392,10 +745,55 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
        switch (test)
        {
        case TORTURE_KRB5_TEST_PLAIN:
+       case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
        case TORTURE_KRB5_TEST_PAC_REQUEST:
+       case TORTURE_KRB5_TEST_AES:
+       case TORTURE_KRB5_TEST_RC4:
+       case TORTURE_KRB5_TEST_AES_RC4:
+       {
+               char *got_principal_string;
+               char *assertion_message;
                torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed");
-               break;
 
+               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);
+
+
+               torture_assert_str_equal(tctx,
+                                        my_creds.server->name.name_string.val[0],
+                                        "krbtgt",
+                                        "Mismatch in name between AS_REP and expected response, expected krbtgt");
+               torture_assert_str_equal(tctx,
+                                        my_creds.server->name.name_string.val[1],
+                                        realm,
+                                        "Mismatch in realm part of krbtgt/ in AS_REP, expected krbtgt/REALM@REALM");
+
+               torture_assert_str_equal(tctx,
+                                        my_creds.server->realm,
+                                        realm,
+                                        "Mismatch in server realm in AS_REP, expected krbtgt/REALM@REALM");
+
+               break;
+       }
        case TORTURE_KRB5_TEST_BREAK_PW:
                torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, "krb5_get_init_creds_password should have failed");
                return true;
@@ -404,6 +802,55 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
                torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_SKEW, "krb5_get_init_creds_password should have failed");
                return true;
 
+       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);
@@ -414,7 +861,8 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx,
 
 static bool torture_krb5_as_req_cmdline(struct torture_context *tctx)
 {
-       return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PLAIN);
+       return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
+                       TORTURE_KRB5_TEST_PLAIN);
 }
 
 static bool torture_krb5_as_req_pac_request(struct torture_context *tctx)
@@ -422,22 +870,68 @@ static bool torture_krb5_as_req_pac_request(struct torture_context *tctx)
        if (torture_setting_bool(tctx, "expect_rodc", false)) {
                torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users");
        }
-       return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PAC_REQUEST);
+       return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
+                       TORTURE_KRB5_TEST_PAC_REQUEST);
 }
 
 static bool torture_krb5_as_req_break_pw(struct torture_context *tctx)
 {
-       return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_BREAK_PW);
+       return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
+                       TORTURE_KRB5_TEST_BREAK_PW);
 }
 
 static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx)
 {
-       return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_CLOCK_SKEW);
+       return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
+                       TORTURE_KRB5_TEST_CLOCK_SKEW);
+}
+
+static bool torture_krb5_as_req_aes(struct torture_context *tctx)
+{
+       return torture_krb5_as_req_creds(tctx,
+                                        popt_get_cmdline_credentials(),
+                                        TORTURE_KRB5_TEST_AES);
+}
+
+static bool torture_krb5_as_req_rc4(struct torture_context *tctx)
+{
+       return torture_krb5_as_req_creds(tctx,
+                                        popt_get_cmdline_credentials(),
+                                        TORTURE_KRB5_TEST_RC4);
 }
 
-NTSTATUS torture_krb5_init(void)
+static bool torture_krb5_as_req_aes_rc4(struct torture_context *tctx)
 {
-       struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "krb5");
+       return torture_krb5_as_req_creds(tctx,
+                                        popt_get_cmdline_credentials(),
+                                        TORTURE_KRB5_TEST_AES_RC4);
+}
+
+/* Checking for the "Orpheus' Lyre" attack */
+static bool torture_krb5_as_req_change_server_out(struct torture_context *tctx)
+{
+       return torture_krb5_as_req_creds(tctx,
+                                        popt_get_cmdline_credentials(),
+                                        TORTURE_KRB5_TEST_CHANGE_SERVER_OUT);
+}
+
+static bool torture_krb5_as_req_change_server_in(struct torture_context *tctx)
+{
+       return torture_krb5_as_req_creds(tctx,
+                                        popt_get_cmdline_credentials(),
+                                        TORTURE_KRB5_TEST_CHANGE_SERVER_IN);
+}
+
+static bool torture_krb5_as_req_change_server_both(struct torture_context *tctx)
+{
+       return torture_krb5_as_req_creds(tctx,
+                                        popt_get_cmdline_credentials(),
+                                        TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH);
+}
+
+NTSTATUS torture_krb5_init(TALLOC_CTX *ctx)
+{
+       struct torture_suite *suite = torture_suite_create(ctx, "krb5");
        struct torture_suite *kdc_suite = torture_suite_create(suite, "kdc");
        suite->description = talloc_strdup(suite, "Kerberos tests");
        kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests");
@@ -454,9 +948,37 @@ NTSTATUS torture_krb5_init(void)
        torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew",
                                      torture_krb5_as_req_clock_skew);
 
+       torture_suite_add_simple_test(kdc_suite,
+                                     "as-req-aes",
+                                     torture_krb5_as_req_aes);
+
+       torture_suite_add_simple_test(kdc_suite,
+                                     "as-req-rc4",
+                                     torture_krb5_as_req_rc4);
+
+       torture_suite_add_simple_test(kdc_suite,
+                                     "as-req-aes-rc4",
+                                     torture_krb5_as_req_aes_rc4);
+
+       /* 
+        * This is in and out of the client. 
+        * Out refers to requests, in refers to replies
+        */
+       torture_suite_add_simple_test(kdc_suite,
+                                     "as-req-change-server-in",
+                                     torture_krb5_as_req_change_server_in);
+
+       torture_suite_add_simple_test(kdc_suite,
+                                     "as-req-change-server-out",
+                                     torture_krb5_as_req_change_server_out);
+
+       torture_suite_add_simple_test(kdc_suite,
+                                     "as-req-change-server-both",
+                                     torture_krb5_as_req_change_server_both);
+
        torture_suite_add_suite(kdc_suite, torture_krb5_canon(kdc_suite));
        torture_suite_add_suite(suite, kdc_suite);
 
-       torture_register_suite(suite);
+       torture_register_suite(ctx, suite);
        return NT_STATUS_OK;
 }