root/source3/libads/authdata.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. check_pac_checksum
  2. decode_pac_data
  3. get_logon_info_from_pac
  4. kerberos_return_pac
  5. kerberos_return_pac_logon_info
  6. kerberos_return_info3_from_pac

   1 /*
   2    Unix SMB/CIFS implementation.
   3    kerberos authorization data (PAC) utility library
   4    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
   5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
   6    Copyright (C) Andrew Tridgell 2001
   7    Copyright (C) Luke Howard 2002-2003
   8    Copyright (C) Stefan Metzmacher 2004-2005
   9    Copyright (C) Guenther Deschner 2005,2007,2008
  10 
  11    This program is free software; you can redistribute it and/or modify
  12    it under the terms of the GNU General Public License as published by
  13    the Free Software Foundation; either version 3 of the License, or
  14    (at your option) any later version.
  15 
  16    This program is distributed in the hope that it will be useful,
  17    but WITHOUT ANY WARRANTY; without even the implied warranty of
  18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19    GNU General Public License for more details.
  20 
  21    You should have received a copy of the GNU General Public License
  22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  23 */
  24 
  25 #include "includes.h"
  26 #include "librpc/gen_ndr/ndr_krb5pac.h"
  27 
  28 #ifdef HAVE_KRB5
  29 
  30 /****************************************************************
  31 ****************************************************************/
  32 
  33 static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
  34                                           DATA_BLOB pac_data,
  35                                           struct PAC_SIGNATURE_DATA *sig,
  36                                           krb5_context context,
  37                                           krb5_keyblock *keyblock)
  38 {
  39         krb5_error_code ret;
  40         krb5_checksum cksum;
  41         krb5_keyusage usage = 0;
  42 
  43         smb_krb5_checksum_from_pac_sig(&cksum, sig);
  44 
  45 #ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */
  46         usage = KRB5_KU_OTHER_CKSUM;
  47 #elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */
  48         usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;
  49 #else
  50 #error UNKNOWN_KRB5_KEYUSAGE
  51 #endif
  52 
  53         ret = smb_krb5_verify_checksum(context,
  54                                        keyblock,
  55                                        usage,
  56                                        &cksum,
  57                                        pac_data.data,
  58                                        pac_data.length);
  59 
  60         if (ret) {
  61                 DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n",
  62                         error_message(ret), ret));
  63                 return ret;
  64         }
  65 
  66         return ret;
  67 }
  68 
  69 /****************************************************************
  70 ****************************************************************/
  71 
  72  NTSTATUS decode_pac_data(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
  73                          DATA_BLOB *pac_data_blob,
  74                          krb5_context context,
  75                          krb5_keyblock *service_keyblock,
  76                          krb5_const_principal client_principal,
  77                          time_t tgs_authtime,
  78                          struct PAC_DATA **pac_data_out)
  79 {
  80         NTSTATUS status;
  81         enum ndr_err_code ndr_err;
  82         krb5_error_code ret;
  83         DATA_BLOB modified_pac_blob;
  84 
  85         NTTIME tgs_authtime_nttime;
  86         krb5_principal client_principal_pac = NULL;
  87         int i;
  88 
  89         struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
  90         struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
  91         struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
  92         struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
  93         struct PAC_LOGON_NAME *logon_name = NULL;
  94         struct PAC_LOGON_INFO *logon_info = NULL;
  95         struct PAC_DATA *pac_data = NULL;
  96         struct PAC_DATA_RAW *pac_data_raw = NULL;
  97 
  98         DATA_BLOB *srv_sig_blob = NULL;
  99         DATA_BLOB *kdc_sig_blob = NULL;
 100 
 101         *pac_data_out = NULL;
 102 
 103         pac_data = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA);
 104         pac_data_raw = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA_RAW);
 105         kdc_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA);
 106         srv_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA);
 107         if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
 108                 return NT_STATUS_NO_MEMORY;
 109         }
 110 
 111         ndr_err = ndr_pull_struct_blob(pac_data_blob, pac_data,
 112                         NULL, pac_data,
 113                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
 114         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 115                 status = ndr_map_error2ntstatus(ndr_err);
 116                 DEBUG(0,("can't parse the PAC: %s\n",
 117                         nt_errstr(status)));
 118                 return status;
 119         }
 120 
 121         if (pac_data->num_buffers < 4) {
 122                 /* we need logon_ingo, service_key and kdc_key */
 123                 DEBUG(0,("less than 4 PAC buffers\n"));
 124                 return NT_STATUS_INVALID_PARAMETER;
 125         }
 126 
 127         ndr_err = ndr_pull_struct_blob(pac_data_blob, pac_data_raw,
 128                                        NULL, pac_data_raw,
 129                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
 130         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 131                 status = ndr_map_error2ntstatus(ndr_err);
 132                 DEBUG(0,("can't parse the PAC: %s\n",
 133                         nt_errstr(status)));
 134                 return status;
 135         }
 136 
 137         if (pac_data_raw->num_buffers < 4) {
 138                 /* we need logon_ingo, service_key and kdc_key */
 139                 DEBUG(0,("less than 4 PAC buffers\n"));
 140                 return NT_STATUS_INVALID_PARAMETER;
 141         }
 142 
 143         if (pac_data->num_buffers != pac_data_raw->num_buffers) {
 144                 /* we need logon_ingo, service_key and kdc_key */
 145                 DEBUG(0,("misparse!  PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
 146                          pac_data->num_buffers, pac_data_raw->num_buffers));
 147                 return NT_STATUS_INVALID_PARAMETER;
 148         }
 149 
 150         for (i=0; i < pac_data->num_buffers; i++) {
 151                 if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
 152                         DEBUG(0,("misparse!  PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
 153                                  i, pac_data->buffers[i].type, pac_data->buffers[i].type));
 154                         return NT_STATUS_INVALID_PARAMETER;
 155                 }
 156                 switch (pac_data->buffers[i].type) {
 157                         case PAC_TYPE_LOGON_INFO:
 158                                 if (!pac_data->buffers[i].info) {
 159                                         break;
 160                                 }
 161                                 logon_info = pac_data->buffers[i].info->logon_info.info;
 162                                 break;
 163                         case PAC_TYPE_SRV_CHECKSUM:
 164                                 if (!pac_data->buffers[i].info) {
 165                                         break;
 166                                 }
 167                                 srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
 168                                 srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
 169                                 break;
 170                         case PAC_TYPE_KDC_CHECKSUM:
 171                                 if (!pac_data->buffers[i].info) {
 172                                         break;
 173                                 }
 174                                 kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
 175                                 kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
 176                                 break;
 177                         case PAC_TYPE_LOGON_NAME:
 178                                 logon_name = &pac_data->buffers[i].info->logon_name;
 179                                 break;
 180                         default:
 181                                 break;
 182                 }
 183         }
 184 
 185         if (!logon_info) {
 186                 DEBUG(0,("PAC no logon_info\n"));
 187                 return NT_STATUS_INVALID_PARAMETER;
 188         }
 189 
 190         if (!logon_name) {
 191                 DEBUG(0,("PAC no logon_name\n"));
 192                 return NT_STATUS_INVALID_PARAMETER;
 193         }
 194 
 195         if (!srv_sig_ptr || !srv_sig_blob) {
 196                 DEBUG(0,("PAC no srv_key\n"));
 197                 return NT_STATUS_INVALID_PARAMETER;
 198         }
 199 
 200         if (!kdc_sig_ptr || !kdc_sig_blob) {
 201                 DEBUG(0,("PAC no kdc_key\n"));
 202                 return NT_STATUS_INVALID_PARAMETER;
 203         }
 204 
 205         /* Find and zero out the signatures, as required by the signing algorithm */
 206 
 207         /* We find the data blobs above, now we parse them to get at the exact portion we should zero */
 208         ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe,
 209                                        NULL, kdc_sig_wipe,
 210                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
 211         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 212                 status = ndr_map_error2ntstatus(ndr_err);
 213                 DEBUG(0,("can't parse the KDC signature: %s\n",
 214                         nt_errstr(status)));
 215                 return status;
 216         }
 217 
 218         ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe,
 219                                        NULL, srv_sig_wipe,
 220                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
 221         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 222                 status = ndr_map_error2ntstatus(ndr_err);
 223                 DEBUG(0,("can't parse the SRV signature: %s\n",
 224                         nt_errstr(status)));
 225                 return status;
 226         }
 227 
 228         /* Now zero the decoded structure */
 229         memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
 230         memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
 231 
 232         /* and reencode, back into the same place it came from */
 233         ndr_err = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw,
 234                                        NULL, kdc_sig_wipe,
 235                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
 236         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 237                 status = ndr_map_error2ntstatus(ndr_err);
 238                 DEBUG(0,("can't repack the KDC signature: %s\n",
 239                         nt_errstr(status)));
 240                 return status;
 241         }
 242         ndr_err = ndr_push_struct_blob(srv_sig_blob, pac_data_raw,
 243                                        NULL, srv_sig_wipe,
 244                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
 245         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 246                 status = ndr_map_error2ntstatus(ndr_err);
 247                 DEBUG(0,("can't repack the SRV signature: %s\n",
 248                         nt_errstr(status)));
 249                 return status;
 250         }
 251 
 252         /* push out the whole structure, but now with zero'ed signatures */
 253         ndr_err = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw,
 254                                        NULL, pac_data_raw,
 255                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
 256         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 257                 status = ndr_map_error2ntstatus(ndr_err);
 258                 DEBUG(0,("can't repack the RAW PAC: %s\n",
 259                         nt_errstr(status)));
 260                 return status;
 261         }
 262 
 263         /* verify by service_key */
 264         ret = check_pac_checksum(mem_ctx,
 265                                  modified_pac_blob, srv_sig_ptr,
 266                                  context,
 267                                  service_keyblock);
 268         if (ret) {
 269                 DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
 270                           error_message(ret)));
 271                 return NT_STATUS_ACCESS_DENIED;
 272         }
 273 
 274         /* Convert to NT time, so as not to loose accuracy in comparison */
 275         unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
 276 
 277         if (tgs_authtime_nttime != logon_name->logon_time) {
 278                 DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
 279                 DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
 280                 DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
 281                 return NT_STATUS_ACCESS_DENIED;
 282         }
 283 
 284         ret = smb_krb5_parse_name_norealm(context, logon_name->account_name,
 285                                     &client_principal_pac);
 286         if (ret) {
 287                 DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
 288                           logon_name->account_name,
 289                           error_message(ret)));
 290                 return NT_STATUS_INVALID_PARAMETER;
 291         }
 292 
 293         if (!smb_krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
 294                 DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
 295                           logon_name->account_name));
 296                 krb5_free_principal(context, client_principal_pac);
 297                 return NT_STATUS_ACCESS_DENIED;
 298         }
 299 
 300         DEBUG(3,("Found account name from PAC: %s [%s]\n",
 301                  logon_info->info3.base.account_name.string,
 302                  logon_info->info3.base.full_name.string));
 303 
 304         DEBUG(10,("Successfully validated Kerberos PAC\n"));
 305 
 306         if (DEBUGLEVEL >= 10) {
 307                 const char *s;
 308                 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_DATA, pac_data);
 309                 if (s) {
 310                         DEBUGADD(10,("%s\n", s));
 311                 }
 312         }
 313 
 314         *pac_data_out = pac_data;
 315 
 316         return NT_STATUS_OK;
 317 }
 318 
 319 /****************************************************************
 320 ****************************************************************/
 321 
 322 struct PAC_LOGON_INFO *get_logon_info_from_pac(struct PAC_DATA *pac_data)
     /* [<][>][^][v][top][bottom][index][help] */
 323 {
 324         int i;
 325 
 326         for (i=0; i < pac_data->num_buffers; i++) {
 327 
 328                 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
 329                         continue;
 330                 }
 331 
 332                 return pac_data->buffers[i].info->logon_info.info;
 333         }
 334 
 335         return NULL;
 336 }
 337 
 338 /****************************************************************
 339 ****************************************************************/
 340 
 341 NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 342                              const char *name,
 343                              const char *pass,
 344                              time_t time_offset,
 345                              time_t *expire_time,
 346                              time_t *renew_till_time,
 347                              const char *cache_name,
 348                              bool request_pac,
 349                              bool add_netbios_addr,
 350                              time_t renewable_time,
 351                              struct PAC_DATA **pac_ret)
 352 {
 353         krb5_error_code ret;
 354         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
 355         DATA_BLOB tkt, ap_rep, sesskey1, sesskey2;
 356         struct PAC_DATA *pac_data = NULL;
 357         char *client_princ_out = NULL;
 358         const char *auth_princ = NULL;
 359         const char *local_service = NULL;
 360         const char *cc = "MEMORY:kerberos_return_pac";
 361 
 362         ZERO_STRUCT(tkt);
 363         ZERO_STRUCT(ap_rep);
 364         ZERO_STRUCT(sesskey1);
 365         ZERO_STRUCT(sesskey2);
 366 
 367         if (!name || !pass) {
 368                 return NT_STATUS_INVALID_PARAMETER;
 369         }
 370 
 371         if (cache_name) {
 372                 cc = cache_name;
 373         }
 374 
 375         if (!strchr_m(name, '@')) {
 376                 auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
 377                         lp_realm());
 378         } else {
 379                 auth_princ = name;
 380         }
 381         NT_STATUS_HAVE_NO_MEMORY(auth_princ);
 382 
 383         local_service = talloc_asprintf(mem_ctx, "%s$@%s",
 384                                         global_myname(), lp_realm());
 385         NT_STATUS_HAVE_NO_MEMORY(local_service);
 386 
 387         ret = kerberos_kinit_password_ext(auth_princ,
 388                                           pass,
 389                                           time_offset,
 390                                           expire_time,
 391                                           renew_till_time,
 392                                           cc,
 393                                           request_pac,
 394                                           add_netbios_addr,
 395                                           renewable_time,
 396                                           &status);
 397         if (ret) {
 398                 DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
 399                         auth_princ, error_message(ret), ret));
 400                 /* status already set */
 401                 goto out;
 402         }
 403 
 404         DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
 405         if (expire_time) {
 406                 DEBUGADD(10,("\tvalid until: %s (%d)\n",
 407                         http_timestring(talloc_tos(), *expire_time),
 408                         (int)*expire_time));
 409         }
 410         if (renew_till_time) {
 411                 DEBUGADD(10,("\trenewable till: %s (%d)\n",
 412                         http_timestring(talloc_tos(), *renew_till_time),
 413                         (int)*renew_till_time));
 414         }
 415 
 416         /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
 417          * in that case fallback to NTLM - gd */
 418 
 419         if (expire_time && renew_till_time &&
 420             (*expire_time == 0) && (*renew_till_time == 0)) {
 421                 return NT_STATUS_INVALID_LOGON_TYPE;
 422         }
 423 
 424 
 425         ret = cli_krb5_get_ticket(local_service,
 426                                   time_offset,
 427                                   &tkt,
 428                                   &sesskey1,
 429                                   0,
 430                                   cc,
 431                                   NULL);
 432         if (ret) {
 433                 DEBUG(1,("failed to get ticket for %s: %s\n",
 434                         local_service, error_message(ret)));
 435                 status = krb5_to_nt_status(ret);
 436                 goto out;
 437         }
 438 
 439         status = ads_verify_ticket(mem_ctx,
 440                                    lp_realm(),
 441                                    time_offset,
 442                                    &tkt,
 443                                    &client_princ_out,
 444                                    &pac_data,
 445                                    &ap_rep,
 446                                    &sesskey2,
 447                                    False);
 448         if (!NT_STATUS_IS_OK(status)) {
 449                 DEBUG(1,("ads_verify_ticket failed: %s\n",
 450                         nt_errstr(status)));
 451                 goto out;
 452         }
 453 
 454         if (!pac_data) {
 455                 DEBUG(1,("no PAC\n"));
 456                 status = NT_STATUS_INVALID_PARAMETER;
 457                 goto out;
 458         }
 459 
 460         *pac_ret = pac_data;
 461 
 462 out:
 463         if (cc != cache_name) {
 464                 ads_kdestroy(cc);
 465         }
 466 
 467         data_blob_free(&tkt);
 468         data_blob_free(&ap_rep);
 469         data_blob_free(&sesskey1);
 470         data_blob_free(&sesskey2);
 471 
 472         TALLOC_FREE(client_princ_out);
 473 
 474         return status;
 475 }
 476 
 477 /****************************************************************
 478 ****************************************************************/
 479 
 480 static NTSTATUS kerberos_return_pac_logon_info(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 481                                                const char *name,
 482                                                const char *pass,
 483                                                time_t time_offset,
 484                                                time_t *expire_time,
 485                                                time_t *renew_till_time,
 486                                                const char *cache_name,
 487                                                bool request_pac,
 488                                                bool add_netbios_addr,
 489                                                time_t renewable_time,
 490                                                struct PAC_LOGON_INFO **logon_info)
 491 {
 492         NTSTATUS status;
 493         struct PAC_DATA *pac_data = NULL;
 494         struct PAC_LOGON_INFO *info = NULL;
 495 
 496         status = kerberos_return_pac(mem_ctx,
 497                                      name,
 498                                      pass,
 499                                      time_offset,
 500                                      expire_time,
 501                                      renew_till_time,
 502                                      cache_name,
 503                                      request_pac,
 504                                      add_netbios_addr,
 505                                      renewable_time,
 506                                      &pac_data);
 507         if (!NT_STATUS_IS_OK(status)) {
 508                 return status;
 509         }
 510 
 511         if (!pac_data) {
 512                 DEBUG(3,("no pac\n"));
 513                 return NT_STATUS_INVALID_USER_BUFFER;
 514         }
 515 
 516         info = get_logon_info_from_pac(pac_data);
 517         if (!info) {
 518                 DEBUG(1,("no logon_info\n"));
 519                 return NT_STATUS_INVALID_USER_BUFFER;
 520         }
 521 
 522         *logon_info = info;
 523 
 524         return NT_STATUS_OK;
 525 }
 526 
 527 /****************************************************************
 528 ****************************************************************/
 529 
 530 NTSTATUS kerberos_return_info3_from_pac(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 531                                         const char *name,
 532                                         const char *pass,
 533                                         time_t time_offset,
 534                                         time_t *expire_time,
 535                                         time_t *renew_till_time,
 536                                         const char *cache_name,
 537                                         bool request_pac,
 538                                         bool add_netbios_addr,
 539                                         time_t renewable_time,
 540                                         struct netr_SamInfo3 **info3)
 541 {
 542         NTSTATUS status;
 543         struct PAC_LOGON_INFO *logon_info = NULL;
 544 
 545         status = kerberos_return_pac_logon_info(mem_ctx,
 546                                                 name,
 547                                                 pass,
 548                                                 time_offset,
 549                                                 expire_time,
 550                                                 renew_till_time,
 551                                                 cache_name,
 552                                                 request_pac,
 553                                                 add_netbios_addr,
 554                                                 renewable_time,
 555                                                 &logon_info);
 556         if (!NT_STATUS_IS_OK(status)) {
 557                 return status;
 558         }
 559 
 560         *info3 = &logon_info->info3;
 561 
 562         return NT_STATUS_OK;
 563 }
 564 #endif

/* [<][>][^][v][top][bottom][index][help] */