Пример #1
0
static krb5_error_code ads_secrets_verify_ticket(krb5_context context,
						krb5_auth_context auth_context,
						krb5_principal host_princ,
						const DATA_BLOB *ticket,
						krb5_ticket **pp_tkt,
						krb5_keyblock **keyblock,
						krb5_error_code *perr)
{
	krb5_error_code ret = 0;
	bool auth_ok = False;
	char *password_s = NULL;
	krb5_data password;
	krb5_enctype enctypes[] = { 
#if defined(ENCTYPE_ARCFOUR_HMAC)
		ENCTYPE_ARCFOUR_HMAC,
#endif
		ENCTYPE_DES_CBC_CRC, 
		ENCTYPE_DES_CBC_MD5, 
		ENCTYPE_NULL
	};
	krb5_data packet;
	int i;

	*pp_tkt = NULL;
	*keyblock = NULL;
	*perr = 0;


	if (!secrets_init()) {
		DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n"));
		*perr = KRB5_CONFIG_CANTOPEN;
		return False;
	}

	password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
	if (!password_s) {
		DEBUG(1,("ads_secrets_verify_ticket: failed to fetch machine password\n"));
		*perr = KRB5_LIBOS_CANTREADPWD;
		return False;
	}

	password.data = password_s;
	password.length = strlen(password_s);

	/* CIFS doesn't use addresses in tickets. This would break NAT. JRA */

	packet.length = ticket->length;
	packet.data = (char *)ticket->data;

	/* We need to setup a auth context with each possible encoding type in turn. */
	for (i=0;enctypes[i];i++) {
		krb5_keyblock *key = NULL;

		if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
			ret = ENOMEM;
			goto out;
		}
	
		if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i], false)) {
			SAFE_FREE(key);
			continue;
		}

		krb5_auth_con_setuseruserkey(context, auth_context, key);

		if (!(ret = krb5_rd_req(context, &auth_context, &packet, 
					NULL,
					NULL, NULL, pp_tkt))) {
			DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n",
				(unsigned int)enctypes[i] ));
			auth_ok = True;
			krb5_copy_keyblock(context, key, keyblock);
			krb5_free_keyblock(context, key);
			break;
		}

		DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
				("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
				(unsigned int)enctypes[i], error_message(ret)));

		/* successfully decrypted but ticket is just not valid at the moment */
		if (ret == KRB5KRB_AP_ERR_TKT_NYV || 
		    ret == KRB5KRB_AP_ERR_TKT_EXPIRED ||
		    ret == KRB5KRB_AP_ERR_SKEW) {
			krb5_free_keyblock(context, key);
			break;
		}

		krb5_free_keyblock(context, key);

	}

 out:
	SAFE_FREE(password_s);
	*perr = ret;
	return auth_ok;
}
Пример #2
0
/* 
 * Serialize krb5_auth_context.
 */
static krb5_error_code
ser_acontext_test(krb5_context kcontext, int verbose)
{
    krb5_error_code	kret;
    krb5_auth_context	actx;
    krb5_address	local_address;
    krb5_address	remote_address;
    krb5_octet		laddr_bytes[16];
    krb5_octet		raddr_bytes[16];
    krb5_keyblock	ukeyblock;
    krb5_octet		keydata[8];
    krb5_authenticator	aent;
    char		clname[128];
    krb5_authdata	*adatalist[3];
    krb5_authdata	adataent;

    actx = (krb5_auth_context) NULL;
    if (!(kret = krb5_auth_con_init(kcontext, &actx)) &&
	!(kret = ser_data(verbose, "> Vanilla auth context",
			  (krb5_pointer) actx,
			  KV5M_AUTH_CONTEXT))) {
	memset(&local_address, 0, sizeof(local_address));
	memset(&remote_address, 0, sizeof(remote_address));
	memset(laddr_bytes, 0, sizeof(laddr_bytes));
	memset(raddr_bytes, 0, sizeof(raddr_bytes));
	local_address.addrtype = ADDRTYPE_INET;
	local_address.length = sizeof(laddr_bytes);
	local_address.contents = laddr_bytes;
	laddr_bytes[0] = 6;
	laddr_bytes[1] = 2;
	laddr_bytes[2] = 69;
	laddr_bytes[3] = 16;
	laddr_bytes[4] = 1;
	laddr_bytes[5] = 0;
	laddr_bytes[6] = 0;
	laddr_bytes[7] = 127;
	remote_address.addrtype = ADDRTYPE_INET;
	remote_address.length = sizeof(raddr_bytes);
	remote_address.contents = raddr_bytes;
	raddr_bytes[0] = 6;
	raddr_bytes[1] = 2;
	raddr_bytes[2] = 70;
	raddr_bytes[3] = 16;
	raddr_bytes[4] = 1;
	raddr_bytes[5] = 0;
	raddr_bytes[6] = 0;
	raddr_bytes[7] = 127;
	if (!(kret = krb5_auth_con_setaddrs(kcontext, actx,
					    &local_address,
					    &remote_address)) &&
	    !(kret = krb5_auth_con_setports(kcontext, actx,
					    &local_address,
					    &remote_address)) &&
	    !(kret = ser_data(verbose, "> Auth context with addrs/ports", 
			      (krb5_pointer) actx,
			      KV5M_AUTH_CONTEXT))) {
	    memset(&ukeyblock, 0, sizeof(ukeyblock));
	    memset(keydata, 0, sizeof(keydata));
	    ukeyblock.enctype = ENCTYPE_DES_CBC_MD5;
	    ukeyblock.length = sizeof(keydata);
	    ukeyblock.contents = keydata;
	    keydata[0] = 0xde;
	    keydata[1] = 0xad;
	    keydata[2] = 0xbe;
	    keydata[3] = 0xef;
	    keydata[4] = 0xfe;
	    keydata[5] = 0xed;
	    keydata[6] = 0xf0;
	    keydata[7] = 0xd;
	    if (!(kret = krb5_auth_con_setuseruserkey(kcontext, actx,
						      &ukeyblock)) &&
		!(kret = ser_data(verbose, "> Auth context with user key",
				  (krb5_pointer) actx,
				  KV5M_AUTH_CONTEXT)) &&
		!(kret = krb5_auth_con_initivector(kcontext, actx)) &&
		!(kret = ser_data(verbose, "> Auth context with new vector",
				  (krb5_pointer) actx,
				  KV5M_AUTH_CONTEXT)) &&
		(krb5_xfree(actx->i_vector), actx->i_vector) &&
		!(kret = krb5_auth_con_setivector(kcontext, actx,
						  (krb5_pointer) print_erep)
		  ) &&
		!(kret = ser_data(verbose, "> Auth context with set vector",
				  (krb5_pointer) actx,
				  KV5M_AUTH_CONTEXT))) {
		/*
		 * Finally, add an authenticator.
		 */
		memset(&aent, 0, sizeof(aent));
		aent.magic = KV5M_AUTHENTICATOR;
		snprintf(clname, sizeof(clname),
			 "help/me/%[email protected]", (int) getpid());
		actx->authentp = &aent;
		if (!(kret = krb5_parse_name(kcontext, clname,
					     &aent.client)) &&
		    !(kret = ser_data(verbose,
				      "> Auth context with authenticator",
				      (krb5_pointer) actx,
				      KV5M_AUTH_CONTEXT))) {
		    adataent.magic = KV5M_AUTHDATA;
		    adataent.ad_type = 123;
		    adataent.length = 128;
		    adataent.contents = (krb5_octet *) stuff;
		    adatalist[0] = &adataent;
		    adatalist[1] = &adataent;
		    adatalist[2] = (krb5_authdata *) NULL;
		    aent.authorization_data = adatalist;
		    if (!(kret = ser_data(verbose,
					  "> Auth context with full auth",
					  (krb5_pointer) actx,
					  KV5M_AUTH_CONTEXT))) {
			if (verbose)
			    printf("* krb5_auth_context test succeeded\n");
		    }
		    krb5_free_principal(kcontext, aent.client);
		}
		actx->authentp = (krb5_authenticator *) NULL;
	    }
	}
    }
    if (actx)
	krb5_auth_con_free(kcontext, actx);
    if (kret)
	printf("* krb5_auth_context test failed\n");
    return(kret);
}
Пример #3
0
DWORD
LwKrb5InitializeUserLoginCredentials(
    IN PCSTR pszUserPrincipalName,
    IN PCSTR pszPassword,
    IN uid_t uid,
    IN gid_t gid,
    IN LW_KRB5_LOGIN_FLAGS Flags,
    IN PCSTR pszServicePrincipal,
    IN PCSTR pszServiceRealm,
    IN PCSTR pszServicePassword,
    OUT PVOID* ppNdrPacInfo,
    OUT size_t* pNdrPacInfoSize,
    OUT PDWORD pdwGoodUntilTime
    )
{
    DWORD dwError = 0;
    krb5_error_code ret = 0;
    krb5_context ctx = NULL;
    krb5_ccache cc = NULL;
    // Free with krb5_free_cred_contents
    krb5_creds credsRequest = {0};
    krb5_creds *pTgsCreds = NULL;
    krb5_ticket *pTgsTicket = NULL;
    krb5_ticket *pDecryptedTgs = NULL;
    krb5_auth_context authContext = NULL;
    krb5_data apReqPacket = {0};
    krb5_keyblock serviceKey = {0};
    krb5_data salt = {0};
    // Do not free
    krb5_data machinePassword = {0};
    krb5_flags flags = 0;
    krb5_int32 authcon_flags = 0;
    BOOLEAN bInLock = FALSE;
    PCSTR pszTempCacheName = NULL;
    PSTR pszTempCachePath = NULL;
    PVOID pNdrPacInfo = NULL;
    size_t ndrPacInfoSize = 0;
    DWORD dwGoodUntilTime = 0;

    ret = krb5_init_context(&ctx);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Generates a new filed based credentials cache in /tmp. The file will
     * be owned by root and only accessible by root.
     */
    ret = krb5_cc_new_unique(
            ctx, 
            "FILE",
            "hint",
            &cc);
    BAIL_ON_KRB_ERROR(ctx, ret);


    if (Flags & LW_KRB5_LOGIN_FLAG_SMART_CARD)
    {
        dwError = LwKrb5GetTgtWithSmartCard(
                pszUserPrincipalName,
                pszPassword,
                krb5_cc_get_name(ctx, cc),
                &dwGoodUntilTime);
    }
    else
    {
        dwError = LwKrb5GetTgt(
                pszUserPrincipalName,
                pszPassword,
                krb5_cc_get_name(ctx, cc),
                &dwGoodUntilTime);
    }

    BAIL_ON_LW_ERROR(dwError);

    ret = krb5_parse_name(ctx, pszServicePrincipal, &credsRequest.server);
    BAIL_ON_KRB_ERROR(ctx, ret);

    ret = krb5_cc_get_principal(ctx, cc, &credsRequest.client);
    BAIL_ON_KRB_ERROR(ctx, ret);
 
    /* Get a TGS for our service using the tgt in the cache */
    ret = krb5_get_credentials(
            ctx,
            0, /*no options (not user to user encryption,
                 and not only cached) */
            cc,
            &credsRequest,
            &pTgsCreds);

    // Don't trust pTgsCreds on an unsuccessful return
    // This may be non-zero due to the krb5 libs following referrals
    // but has been freed in the krb5 libs themselves and any useful
    // tickets have already been cached.
    if (ret != 0) {
        pTgsCreds = NULL;
    }
    
    BAIL_ON_KRB_ERROR(ctx, ret);

    //No need to store the tgs in the cc. Kerberos does that automatically

    /* Generate an ap_req message, but don't send it anywhere. Just decode it
     * immediately. This is the only way to get kerberos to decrypt the tgs
     * using public APIs */
    ret = krb5_mk_req_extended(
            ctx,
            &authContext,
            0, /* no options necessary */
            NULL, /* since this isn't a real ap_req, we don't have any
                     supplemental data to send with it. */
            pTgsCreds,
            &apReqPacket);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Decode (but not decrypt) the tgs ticket so that we can figure out
     * which encryption type was used in it. */
    ret = krb5_decode_ticket(&pTgsCreds->ticket, &pTgsTicket);

    /* The TGS ticket is encrypted with the machine password and salted with
     * the service principal. pszServicePrincipal could probably be used
     * directly, but it's safer to unparse pTgsCreds->server, because the KDC
     * sent that to us.
     */
    salt.magic = KV5M_DATA;
    ret = krb5_unparse_name(
            ctx,
            pTgsCreds->server,
            &salt.data);
    BAIL_ON_KRB_ERROR(ctx, ret);
    salt.length = strlen(salt.data);

    machinePassword.magic = KV5M_DATA;
    machinePassword.data = (PSTR)pszServicePassword,
    machinePassword.length = strlen(pszServicePassword),

    /* Generate a key to decrypt the TGS */
    ret = krb5_c_string_to_key(
            ctx,
            pTgsTicket->enc_part.enctype,
            &machinePassword,
            &salt,
            &serviceKey);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Typically krb5_rd_req would decode the AP_REQ using the keytab, but
     * we don't want to depend on the keytab. As a side effect of kerberos'
     * user to user authentication support, if a key is explictly set on the
     * auth context, that key will be used to decrypt the TGS instead of the
     * keytab.
     *
     * By manually generating the key and setting it, we don't require
     * a keytab.
     */
    if (authContext != NULL)
    {
        ret = krb5_auth_con_free(ctx, authContext);
        BAIL_ON_KRB_ERROR(ctx, ret);
    }
    
    ret = krb5_auth_con_init(ctx, &authContext);
    BAIL_ON_KRB_ERROR(ctx, ret);

    ret = krb5_auth_con_setuseruserkey(
            ctx,
            authContext,
            &serviceKey);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Disable replay detection which is unnecessary and
     * can fail when authenticating large numbers of users.
     */
    krb5_auth_con_getflags(ctx,
                           authContext,
                           &authcon_flags);
    krb5_auth_con_setflags(ctx,
                           authContext,
                           authcon_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);


    if (pszServiceRealm)
    {
        ret = krb5_set_default_realm(ctx, pszServiceRealm);
        BAIL_ON_KRB_ERROR(ctx, ret);
    }

    /* This decrypts the TGS. As a side effect it ensures that the KDC that
     * the user's TGT came from is in the same realm that the machine was
     * joined to (this prevents users from spoofing the KDC).
     */
    ret = krb5_rd_req(
            ctx,
            &authContext,
            &apReqPacket,
            pTgsCreds->server,
            NULL, /* we're not using the keytab */
            &flags,
            &pDecryptedTgs);
    BAIL_ON_KRB_ERROR(ctx, ret);

    dwError = LwKrb5FindPac(
        ctx,
        pDecryptedTgs,
        &serviceKey,
        &pNdrPacInfo,
        &ndrPacInfoSize);
    BAIL_ON_LW_ERROR(dwError);

    if (Flags & LW_KRB5_LOGIN_FLAG_UPDATE_CACHE)
    {
        /* 1. Copy old credentials from the existing user creds cache to
         *      the temporary cache.
         * 2. Delete the existing creds cache.
         * 3. Move the temporary cache file into the final path.
         */
        dwError = pthread_mutex_lock(&gLwKrb5State.UserCacheMutex);
        BAIL_ON_LW_ERROR(dwError);
        bInLock = TRUE;

        dwError = LwKrb5CopyFromUserCache(
                    ctx,
                    cc,
                    uid
                    );
        BAIL_ON_LW_ERROR(dwError);

        pszTempCacheName = krb5_cc_get_name(ctx, cc);
        if (!strncasecmp(pszTempCacheName, "FILE:", sizeof("FILE:")-1)) {
            pszTempCacheName += sizeof("FILE:") - 1;
        }

        dwError = LwAllocateString(pszTempCacheName, &pszTempCachePath);
        BAIL_ON_LW_ERROR(dwError);

        krb5_cc_close(ctx, cc);
        // Just to make sure no one accesses this now invalid pointer
        cc = NULL;

        dwError = LwKrb5MoveCCacheToUserPath(
                    ctx,
                    pszTempCachePath,
                    uid,
                    gid);
        if (dwError != LW_ERROR_SUCCESS)
        {
            /* Let the user login, even if we couldn't create the ccache for
             * them. Possible causes are:
             * 1. /tmp is readonly
             * 2. Another user maliciously setup a weird file (such as a
             *    directory) where the ccache would go.
             * 3. Someone created a ccache in the small window after we delete
             *    the old one and before we move in the new one.
             */
            LW_LOG_WARNING("Unable to set up credentials cache with tgt for uid %ld", (long)uid);
            dwError = LwRemoveFile(pszTempCachePath);
            BAIL_ON_LW_ERROR(dwError);
        }
    }

error:
    if (dwError)
    {
        LW_SAFE_FREE_MEMORY(pNdrPacInfo);
        ndrPacInfoSize = 0;
        dwGoodUntilTime = 0;
    }

    if (ctx)
    {
        // This function skips fields which are NULL
        krb5_free_cred_contents(ctx, &credsRequest);
    
        if (pTgsCreds != NULL)
        {
            krb5_free_creds(ctx, pTgsCreds);
        }
        
        if (pTgsTicket != NULL)
        {
            krb5_free_ticket(ctx, pTgsTicket);
        }
        
        if (pDecryptedTgs != NULL)
        {
            krb5_free_ticket(ctx, pDecryptedTgs);
        }
        
        if (authContext != NULL)
        {
            krb5_auth_con_free(ctx, authContext);
        }
        
        krb5_free_data_contents(ctx, &apReqPacket);
        krb5_free_data_contents(ctx, &salt);
        krb5_free_keyblock_contents(ctx, &serviceKey);

        if (cc != NULL)
        {
            krb5_cc_destroy(ctx, cc);
        }
        krb5_free_context(ctx);
    }
    if (bInLock)
    {
        pthread_mutex_unlock(&gLwKrb5State.UserCacheMutex);
    }
    LW_SAFE_FREE_STRING(pszTempCachePath);

    *ppNdrPacInfo = pNdrPacInfo;
    *pNdrPacInfoSize = ndrPacInfoSize;
    *pdwGoodUntilTime = dwGoodUntilTime;

    return dwError;
}
Пример #4
0
static krb5_error_code ads_secrets_verify_ticket(krb5_context context,
						krb5_auth_context auth_context,
						krb5_principal host_princ,
						const DATA_BLOB *ticket,
						krb5_ticket **pp_tkt,
						krb5_keyblock **keyblock,
						krb5_error_code *perr)
{
	krb5_error_code ret = 0;
	bool auth_ok = False;
	bool cont = true;
	char *password_s = NULL;
	/* Let's make some room for 2 password (old and new)*/
	krb5_data passwords[2];
	krb5_enctype enctypes[] = {
#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
		ENCTYPE_AES256_CTS_HMAC_SHA1_96,
#endif
#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
		ENCTYPE_AES128_CTS_HMAC_SHA1_96,
#endif
		ENCTYPE_ARCFOUR_HMAC,
		ENCTYPE_DES_CBC_CRC,
		ENCTYPE_DES_CBC_MD5,
		ENCTYPE_NULL
	};
	krb5_data packet;
	int i, j;

	*pp_tkt = NULL;
	*keyblock = NULL;
	*perr = 0;

	ZERO_STRUCT(passwords);

	if (!secrets_init()) {
		DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n"));
		*perr = KRB5_CONFIG_CANTOPEN;
		return False;
	}

	password_s = secrets_fetch_machine_password(lp_workgroup(),
						    NULL, NULL);
	if (!password_s) {
		DEBUG(1,(__location__ ": failed to fetch machine password\n"));
		*perr = KRB5_LIBOS_CANTREADPWD;
		return False;
	}

	passwords[0].data = password_s;
	passwords[0].length = strlen(password_s);

	password_s = secrets_fetch_prev_machine_password(lp_workgroup());
	if (password_s) {
		DEBUG(10, (__location__ ": found previous password\n"));
		passwords[1].data = password_s;
		passwords[1].length = strlen(password_s);
	}

	/* CIFS doesn't use addresses in tickets. This would break NAT. JRA */

	packet.length = ticket->length;
	packet.data = (char *)ticket->data;

	/* We need to setup a auth context with each possible encoding type
	 * in turn. */
	for (j=0; j<2 && passwords[j].length; j++) {

		for (i=0;enctypes[i];i++) {
			krb5_keyblock *key = NULL;

			if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
				ret = ENOMEM;
				goto out;
			}

			if (create_kerberos_key_from_string(context,
						host_princ, &passwords[j],
						key, enctypes[i], false)) {
				SAFE_FREE(key);
				continue;
			}

			krb5_auth_con_setuseruserkey(context,
							auth_context, key);

			if (!(ret = krb5_rd_req(context, &auth_context,
						&packet, NULL, NULL,
						NULL, pp_tkt))) {
				DEBUG(10, (__location__ ": enc type [%u] "
					   "decrypted message !\n",
					   (unsigned int)enctypes[i]));
				auth_ok = True;
				cont = false;
				krb5_copy_keyblock(context, key, keyblock);
				krb5_free_keyblock(context, key);
				break;
			}

			DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
				(__location__ ": enc type [%u] failed to "
				 "decrypt with error %s\n",
				 (unsigned int)enctypes[i],
				 error_message(ret)));

			/* successfully decrypted but ticket is just not
			 * valid at the moment */
			if (ret == KRB5KRB_AP_ERR_TKT_NYV ||
			    ret == KRB5KRB_AP_ERR_TKT_EXPIRED ||
			    ret == KRB5KRB_AP_ERR_SKEW) {
				krb5_free_keyblock(context, key);
				cont = false;
				break;
			}

			krb5_free_keyblock(context, key);
		}
		if (!cont) {
			/* If we found a valid pass then no need to try
			 * the next one or we have invalid ticket so no need
			 * to try next password*/
			break;
		}
	}

 out:
	SAFE_FREE(passwords[0].data);
	SAFE_FREE(passwords[1].data);
	*perr = ret;
	return auth_ok;
}