PATCH: krb5-1.2.4 Set security on file cache in NT/2000

Alistair Mackay ali_m_000 at hotmail.com
Mon Apr 22 12:21:12 EDT 2002


All,


The following patch affords NTFS security on a file cache should you
choose to use one (to get Oracle 8 to talk to MIT in NT, file caching
is the only way). The way it's set up now sets read/write access to
the owner of the process creating the cache file (ie by running Leash
or kinit) and the world group "Everyone" delete access so that if
another user logs in, the existing cache may be deleted and hence
overwritten. Leash complains a little about the fact that it can't
read the file (permission denied), but as mentioned, it will still get
deleted and a new one created. You can play with the permissions for
owner and everyone by changing the #define's
OWNER_CCACHE_ACCESS_RIGHTS and WORLD_CCACHE_ACCESS_RIGHTS near the top
of the file to other valid values from winnt.h



Replace src/lib/krb5/ccache/file/fcc_init.c with the following and
rebuild the DLL's

===== fcc_init.c BEGIN ====

/*
 * lib/krb5/ccache/file/fcc_init.c
 *
 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization
contemplating
 *   export to obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity
pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T.
software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without
express
 * or implied warranty.
 * 
 *
 * This file contains the source code for krb5_fcc_initialize.
 */


#include <errno.h>
#include "fcc.h"

#ifdef _WIN32

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

/*
 * Set NT operating system security on new ccache file
 *
 * Will attempt to set RW permission for the current user, and delete
 * permission for "Everyone". Since ccache filenames in Windows are
not
 * by default created with some identifier for the current uid(),
 * somebody else logging on at the workstation needs to be able to
 * delete the ccache to initialse their own
 */

/*
 * Adapted from Microsoft KB articles
 * "HOWTO: Add an Access-Allowed ACE to a File (Q102102)" and
 * "HOWTO: Retrieve Current User and Domain Names on Windows NT,
Windows 2000, or Windows XP (Q111544)"
 * and the MSDN reference on "well known" SIDs
 *
 * The two KB artices were combined to get a routine that gets the SID
 * of the current user (running this process), create a NULL DACL
 * and add an access allowed ACE to this DACL giving RW access to this
user
 * only + another ACE affording delete to everyone else.
 */

/* 
 * Must use Windows mem manager to ensure security structs are DWORD
aligned
 */

#define myheapalloc(x) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
x))
#define myheapfree(x)  (HeapFree(GetProcessHeap(), 0, x))


/* File access rights for the owner of the ccache */
#define OWNER_CCACHE_ACCESS_RIGHTS (GENERIC_READ | GENERIC_WRITE)

/*
 *  ..and for the rest of the world. If a fixed name ccfile is used
 * (ie not using USERNAME from environment or some such), the rest of
the
 * world must be able to overwrite an existing ccache when they kinit
 */

#define WORLD_CCACHE_ACCESS_RIGHTS (DELETE)

int
set_windows_security (char *lpszFileName)
{
  /*
   * Values for errno, I've best guessed. Suggestions anyone?
   */

  // SID variables.
  HANDLE hToken = NULL;
  PTOKEN_USER ptiUser = NULL;
  DWORD cbti = 0;
  SID_IDENTIFIER_AUTHORITY sia = SECURITY_WORLD_SID_AUTHORITY;
  PSID pSidEveryone = NULL;

  // New SD variables.
  SECURITY_DESCRIPTOR newSD;

  // ACL variables.
  ACL_SIZE_INFORMATION AclInfo;

  // New ACL variables.
  PACL pNewACL = NULL;
  DWORD cbNewACL = 0;

  // Assume function will fail.
  int retval = -1;

  SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;


  // 
  // STEP 1: Get SID of the account this process is running as.
  // 

  // Get the calling thread's access token.
  if (!OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, TRUE,
&hToken))
    {

      if (GetLastError () != ERROR_NO_TOKEN)
	goto cleanup;

      // Retry against process token if no thread token exists.
      if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY,
&hToken))
	goto cleanup;
    }

  // Obtain the size of the user information in the token.
  if (GetTokenInformation (hToken, TokenUser, NULL, 0, &cbti))
    {
      // Call should have failed due to zero-length buffer.
      // ie we don't expect to get here!
      goto cleanup;
    }
  else
    {

      // Call should have failed due to zero-length buffer.
      if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
	goto cleanup;
    }

  // Allocate buffer for user information in the token.
  ptiUser = (PTOKEN_USER) myheapalloc (cbti);
  if (!ptiUser)
    goto cleanup;

  // Retrieve the user information from the token.
  if (!GetTokenInformation (hToken, TokenUser, ptiUser, cbti, &cbti))
    goto cleanup;

  // 
  // Create a "well known" SID for the group "Everyone"
  //

  if (!AllocateAndInitializeSid
      (&sia, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0,
&pSidEveryone))
    {
      errno = ENOMEM;
      goto cleanup;
    }

  // 
  // Initialize new SD.
  // 
  if (!InitializeSecurityDescriptor (&newSD,
SECURITY_DESCRIPTOR_REVISION))
    {
      errno = EINVAL;
      goto cleanup;
    }
  // 
  // Get size information for DACL.
  // 
  AclInfo.AceCount = 0;		// Assume NULL DACL.
  AclInfo.AclBytesFree = 0;
  AclInfo.AclBytesInUse = sizeof (ACL);


  // 
  // Compute size needed for the new ACL.
  // We're adding two ACEs to it 
  // 
  cbNewACL = sizeof (ACL) + (2 * sizeof (ACCESS_ALLOWED_ACE)) +
GetLengthSid (ptiUser->User.Sid) + GetLengthSid (pSidEveryone) - (2 *
sizeof (DWORD));	// ACE struct allows for first DWORD of contained
SID.

  // 
  // Allocate memory for new ACL.
  // 
  pNewACL = (PACL) myheapalloc (cbNewACL);
  if (!pNewACL)
    {
      errno = ENOMEM;
      goto cleanup;
    }

  // 
  // Initialize the new ACL.
  // 
  if (!InitializeAcl (pNewACL, cbNewACL, ACL_REVISION2))
    {
      errno = EINVAL;
      goto cleanup;
    }

  // 
  // Add the access-allowed ACE for the current user to the new DACL.
  //

  if (!AddAccessAllowedAce
      (pNewACL, ACL_REVISION2, OWNER_CCACHE_ACCESS_RIGHTS,
ptiUser->User.Sid))
    {
      errno = EINVAL;
      goto cleanup;
    }

  //
  // Add access-allowed ACE for eveyone.
  //
  if (!AddAccessAllowedAce
      (pNewACL, ACL_REVISION2, WORLD_CCACHE_ACCESS_RIGHTS,
pSidEveryone))
    {
      errno = EINVAL;
      goto cleanup;
    }

  // 
  // Set the new DACL to the new SD.
  // 
  if (!SetSecurityDescriptorDacl (&newSD, TRUE, pNewACL, FALSE))
    {
      errno = EINVAL;
      goto cleanup;
    }
  // 
  // Set the new SD to the File.
  // 
  if (!SetFileSecurity (lpszFileName, secInfo, &newSD))
    {
      if (GetLastError () == ERROR_ACCESS_DENIED)
	errno = EACCES;
      else
	errno = EINVAL;

      goto cleanup;
    }

  retval = 0;


cleanup:

  // 
  // Free allocated memory
  // 

  if (hToken)
    CloseHandle (hToken);

  if (ptiUser)
    myheapfree (ptiUser);

  if (pSidEveryone)
    FreeSid (pSidEveryone);

  if (pNewACL)
    myheapfree (pNewACL);

  return retval;
}



#endif


/*
 * Modifies:
 * id
 *
 * Effects:
 * Creates/refreshes the file cred cache id.  If the cache exists, its
 * contents are destroyed.
 *
 * Errors:
 * system errors
 * permission errors
 */
krb5_error_code KRB5_CALLCONV
krb5_fcc_initialize (context, id, princ)
     krb5_context context;
     krb5_ccache id;
     krb5_principal princ;
{
  krb5_error_code kret = 0;
  int reti = 0;

  MAYBE_OPEN (context, id, FCC_OPEN_AND_ERASE);

#ifdef _WIN32
  reti = set_windows_security (((krb5_fcc_data *)
id->data)->filename);
#else
#ifndef HAVE_FCHMOD
#ifdef HAVE_CHMOD
  reti = chmod (((krb5_fcc_data *) id->data)->filename, S_IREAD |
S_IWRITE);
#endif
#else
  reti = fchmod (((krb5_fcc_data *) id->data)->fd, S_IREAD |
S_IWRITE);
#endif
#endif /* _WIN32 */

  if (reti == -1)
    {
      kret = krb5_fcc_interpret (context, errno);
      MAYBE_CLOSE (context, id, kret);
      return kret;
    }
  kret = krb5_fcc_store_principal (context, id, princ);

  MAYBE_CLOSE (context, id, kret);
  krb5_change_cache ();
  return kret;
}


===== fcc_init.c END ====


Enjoy!



More information about the Kerberos mailing list