Line data Source code
1 : /* 2 : * Unix SMB/CIFS implementation. 3 : * 4 : * Copyright (c) 2015 Andreas Schneider <asn@samba.org> 5 : * 6 : * This program is free software; you can redistribute it and/or modify 7 : * it under the terms of the GNU General Public License as published by 8 : * the Free Software Foundation; either version 3 of the License, or 9 : * (at your option) any later version. 10 : * 11 : * This program is distributed in the hope that it will be useful, 12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 : * GNU General Public License for more details. 15 : * 16 : * You should have received a copy of the GNU General Public License 17 : * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 : */ 19 : 20 : #include "includes.h" 21 : #include "system/kerberos.h" 22 : #include "source4/auth/kerberos/kerberos.h" 23 : #include "auth/kerberos/pac_utils.h" 24 : 25 : #include "librpc/gen_ndr/irpc.h" 26 : #include "lib/messaging/irpc.h" 27 : #include "source4/librpc/gen_ndr/ndr_irpc.h" 28 : #include "source4/librpc/gen_ndr/irpc.h" 29 : 30 : #include "librpc/gen_ndr/ndr_krb5pac.h" 31 : 32 : #include "source4/samba/process_model.h" 33 : #include "lib/param/param.h" 34 : 35 : #include "samba_kdc.h" 36 : #include "db-glue.h" 37 : #include "sdb.h" 38 : #include "mit_kdc_irpc.h" 39 : 40 : #undef DBGC_CLASS 41 : #define DBGC_CLASS DBGC_KERBEROS 42 : 43 : struct mit_kdc_irpc_context { 44 : struct task_server *task; 45 : krb5_context krb5_context; 46 : struct samba_kdc_db_context *db_ctx; 47 : }; 48 : 49 100 : static NTSTATUS netr_samlogon_generic_logon(struct irpc_message *msg, 50 : struct kdc_check_generic_kerberos *r) 51 : { 52 : struct PAC_Validate pac_validate; 53 : DATA_BLOB pac_chksum; 54 : struct PAC_SIGNATURE_DATA pac_kdc_sig; 55 : struct mit_kdc_irpc_context *mki_ctx = 56 100 : talloc_get_type(msg->private_data, 57 : struct mit_kdc_irpc_context); 58 : enum ndr_err_code ndr_err; 59 : int code; 60 : krb5_principal principal; 61 100 : struct sdb_entry sentry = {}; 62 : struct sdb_keys skeys; 63 : unsigned int i; 64 100 : const uint8_t *d = NULL; 65 : 66 : /* There is no reply to this request */ 67 100 : r->out.generic_reply = data_blob(NULL, 0); 68 : 69 : ndr_err = 70 100 : ndr_pull_struct_blob(&r->in.generic_request, 71 : msg, 72 : &pac_validate, 73 : (ndr_pull_flags_fn_t)ndr_pull_PAC_Validate); 74 100 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 75 0 : return NT_STATUS_INVALID_PARAMETER; 76 : } 77 : 78 100 : if (pac_validate.MessageType != NETLOGON_GENERIC_KRB5_PAC_VALIDATE) { 79 : /* 80 : * We don't implement any other message types - such as 81 : * certificate validation - yet 82 : */ 83 0 : return NT_STATUS_INVALID_PARAMETER; 84 : } 85 : 86 100 : if ((pac_validate.ChecksumAndSignature.length != 87 100 : (pac_validate.ChecksumLength + pac_validate.SignatureLength)) || 88 60 : (pac_validate.ChecksumAndSignature.length < 89 60 : pac_validate.ChecksumLength) || 90 60 : (pac_validate.ChecksumAndSignature.length < 91 60 : pac_validate.SignatureLength)) { 92 40 : return NT_STATUS_INVALID_PARAMETER; 93 : } 94 : 95 : /* PAC Checksum */ 96 60 : pac_chksum = data_blob_const(pac_validate.ChecksumAndSignature.data, 97 60 : pac_validate.ChecksumLength); 98 : 99 : /* Create the krbtgt principal */ 100 60 : code = smb_krb5_make_principal(mki_ctx->krb5_context, 101 : &principal, 102 60 : lpcfg_realm(mki_ctx->task->lp_ctx), 103 : "krbtgt", 104 60 : lpcfg_realm(mki_ctx->task->lp_ctx), 105 : NULL); 106 60 : if (code != 0) { 107 0 : DBG_ERR("Failed to create krbtgt@%s principal!\n", 108 : lpcfg_realm(mki_ctx->task->lp_ctx)); 109 0 : return NT_STATUS_NO_MEMORY; 110 : } 111 : 112 : /* Get the krbtgt from the DB */ 113 60 : code = samba_kdc_fetch(mki_ctx->krb5_context, 114 : mki_ctx->db_ctx, 115 : principal, 116 : SDB_F_GET_KRBTGT | SDB_F_DECRYPT, 117 : 0, 118 : &sentry); 119 60 : krb5_free_principal(mki_ctx->krb5_context, principal); 120 60 : if (code != 0) { 121 0 : DBG_ERR("Failed to fetch krbtgt@%s principal entry!\n", 122 : lpcfg_realm(mki_ctx->task->lp_ctx)); 123 0 : return NT_STATUS_LOGON_FAILURE; 124 : } 125 : 126 : /* PAC Signature */ 127 60 : pac_kdc_sig.type = pac_validate.SignatureType; 128 : 129 60 : d = &pac_validate.ChecksumAndSignature.data[pac_validate.ChecksumLength]; 130 : pac_kdc_sig.signature = 131 60 : data_blob_const(d, pac_validate.SignatureLength); 132 : 133 : /* 134 : * Brute force variant because MIT KRB5 doesn't provide a function like 135 : * krb5_checksum_to_enctype(). 136 : */ 137 60 : skeys = sentry.keys; 138 : 139 60 : code = EINVAL; 140 148 : for (i = 0; i < skeys.len; i++) { 141 108 : krb5_keyblock krbtgt_keyblock = skeys.val[i].key; 142 : 143 108 : code = check_pac_checksum(pac_chksum, 144 : &pac_kdc_sig, 145 : mki_ctx->krb5_context, 146 : &krbtgt_keyblock); 147 108 : if (code == 0) { 148 20 : break; 149 : } 150 : } 151 : 152 60 : sdb_entry_free(&sentry); 153 : 154 60 : if (code != 0) { 155 40 : return NT_STATUS_LOGON_FAILURE; 156 : } 157 : 158 20 : return NT_STATUS_OK; 159 : } 160 : 161 17 : NTSTATUS samba_setup_mit_kdc_irpc(struct task_server *task) 162 : { 163 17 : struct samba_kdc_base_context base_ctx = {}; 164 : struct mit_kdc_irpc_context *mki_ctx; 165 : NTSTATUS status; 166 : int code; 167 : 168 17 : mki_ctx = talloc_zero(task, struct mit_kdc_irpc_context); 169 17 : if (mki_ctx == NULL) { 170 0 : return NT_STATUS_NO_MEMORY; 171 : } 172 17 : mki_ctx->task = task; 173 : 174 17 : base_ctx.ev_ctx = task->event_ctx; 175 17 : base_ctx.lp_ctx = task->lp_ctx; 176 : 177 : /* db-glue.h */ 178 17 : status = samba_kdc_setup_db_ctx(mki_ctx, 179 : &base_ctx, 180 : &mki_ctx->db_ctx); 181 17 : if (!NT_STATUS_IS_OK(status)) { 182 0 : return status; 183 : } 184 : 185 17 : code = smb_krb5_init_context_basic(mki_ctx, 186 : task->lp_ctx, 187 : &mki_ctx->krb5_context); 188 17 : if (code != 0) { 189 0 : return NT_STATUS_INTERNAL_ERROR; 190 : } 191 : 192 17 : status = IRPC_REGISTER(task->msg_ctx, 193 : irpc, 194 : KDC_CHECK_GENERIC_KERBEROS, 195 : netr_samlogon_generic_logon, 196 : mki_ctx); 197 17 : if (!NT_STATUS_IS_OK(status)) { 198 0 : return status; 199 : } 200 : 201 17 : irpc_add_name(task->msg_ctx, "kdc_server"); 202 : 203 17 : return status; 204 : }