/ kcm / client.c
client.c
  1  /*
  2   * Copyright (c) 2005, PADL Software Pty Ltd.
  3   * All rights reserved.
  4   *
  5   * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
  6   *
  7   * Redistribution and use in source and binary forms, with or without
  8   * modification, are permitted provided that the following conditions
  9   * are met:
 10   *
 11   * 1. Redistributions of source code must retain the above copyright
 12   *    notice, this list of conditions and the following disclaimer.
 13   *
 14   * 2. Redistributions in binary form must reproduce the above copyright
 15   *    notice, this list of conditions and the following disclaimer in the
 16   *    documentation and/or other materials provided with the distribution.
 17   *
 18   * 3. Neither the name of PADL Software nor the names of its contributors
 19   *    may be used to endorse or promote products derived from this software
 20   *    without specific prior written permission.
 21   *
 22   * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
 23   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 24   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 25   * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
 26   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 28   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 29   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 32   * SUCH DAMAGE.
 33   */
 34  
 35  #include "kcm_locl.h"
 36  #include <pwd.h>
 37  
 38  krb5_error_code
 39  kcm_ccache_resolve_client(krb5_context context,
 40  			  kcm_client *client,
 41  			  kcm_operation opcode,
 42  			  const char *name,
 43  			  kcm_ccache *ccache)
 44  {
 45      krb5_error_code ret;
 46  
 47      ret = kcm_ccache_resolve_by_name(context, name, ccache);
 48      if (ret) {
 49  	kcm_log(1, "Failed to resolve cache %s", name);
 50  	return ret;
 51      }
 52  
 53      ret = kcm_access(context, client, opcode, *ccache);
 54      if (ret) {
 55  	ret = KRB5_FCC_NOFILE; /* don't disclose */
 56  	kcm_release_ccache(context, *ccache);
 57      }
 58  
 59      return ret;
 60  }
 61  
 62  krb5_error_code
 63  kcm_ccache_destroy_client(krb5_context context,
 64  			  kcm_client *client,
 65  			  const char *name)
 66  {
 67      krb5_error_code ret;
 68      kcm_ccache ccache;
 69  
 70      ret = kcm_ccache_resolve_by_name(context, name, &ccache);
 71      if (ret) {
 72  	kcm_log(1, "Failed to resolve cache %s", name);
 73  	return ret;
 74      }
 75  
 76      ret = kcm_access(context, client, KCM_OP_DESTROY, ccache);
 77      kcm_release_ccache(context, ccache);
 78      if (ret)
 79  	return ret;
 80  
 81      return kcm_ccache_destroy(context, name);
 82  }
 83  
 84  krb5_error_code
 85  kcm_ccache_new_client(krb5_context context,
 86  		      kcm_client *client,
 87  		      const char *name,
 88  		      kcm_ccache *ccache_p)
 89  {
 90      krb5_error_code ret;
 91      kcm_ccache ccache;
 92  
 93      ret = kcm_ccache_resolve_by_name(context, name, &ccache);
 94      if (ret == 0) {
 95  	if ((ccache->uid != client->uid) && !CLIENT_IS_ROOT(client))
 96  	    return KRB5_FCC_PERM;
 97      } else if (ret != KRB5_FCC_NOFILE && !(CLIENT_IS_ROOT(client) && ret == KRB5_FCC_PERM)) {
 98  		return ret;
 99      }
100  
101      if (ret == KRB5_FCC_NOFILE) {
102  	ret = kcm_ccache_new_internal(context, name, client->uid, client->session, &ccache);
103  	if (ret) {
104  	    kcm_log(1, "Failed to initialize cache %s", name);
105  	    return ret;
106  	}
107  
108  	/* 
109  	 * add notification when the session goes away, so we can
110  	 * remove the credential
111  	 */
112  	kcm_session_add(client->session);
113  
114      } else {
115  	ret = kcm_zero_ccache_data(context, ccache);
116  	if (ret) {
117  	    kcm_log(1, "Failed to empty cache %s", name);
118  	    kcm_release_ccache(context, ccache);
119  	    return ret;
120  	}
121  	heim_ipc_event_cancel(ccache->renew_event);
122  	heim_ipc_event_cancel(ccache->expire_event);
123      }
124  
125      ret = kcm_access(context, client, KCM_OP_INITIALIZE, ccache);
126      if (ret) {
127  	kcm_release_ccache(context, ccache);
128  	kcm_ccache_destroy(context, name);
129  	return ret;
130      }
131  
132      /*
133       * Finally, if the user is root and the cache was created under
134       * another user's name, chown the cache to that user.
135       */
136      if (CLIENT_IS_ROOT(client)) {
137  	unsigned long uid;
138  	int matches = sscanf(name,"%ld:",&uid);
139  	if (matches == 0)
140  	    matches = sscanf(name,"%ld",&uid);
141  	if (matches == 1) {
142  	    kcm_chown(context, client, ccache, (uid_t)uid);
143  	}
144      }
145  
146      *ccache_p = ccache;
147      return 0;
148  }
149  
150  const char *
151  kcm_client_get_execpath(kcm_client *client)
152  {
153      if (client->execpath[0] == '\0') {
154  	int ret = proc_pidpath(client->pid, client->execpath, sizeof(client->execpath));
155  	if (ret != -1)
156  	    client->execpath[sizeof(client->execpath) - 1] = '\0';
157  	else {
158  	    /* failed, lets not try again */
159  	    client->execpath[0] = 0x01;
160  	    client->execpath[1] = 0x0;
161  	}
162      }
163      if (client->execpath[0] != '/')
164  	return NULL;
165  
166      return client->execpath;
167  }
168  
169  krb5_boolean
170  krb5_has_entitlement(audit_token_t token, CFStringRef entitlement)
171  {
172      
173      SecTaskRef task = SecTaskCreateWithAuditToken(NULL, token);
174      if (!task) {
175  	kcm_log(1, "unable to create task for audit token");
176  	return false;
177      }
178      
179      CFErrorRef error = NULL;
180      CFTypeRef hasEntitlement = SecTaskCopyValueForEntitlement(task, entitlement, &error);
181      CFRELEASE_NULL(task);
182      if (hasEntitlement == NULL || CFGetTypeID(hasEntitlement) != CFBooleanGetTypeID() || !CFBooleanGetValue(hasEntitlement)) {
183  	if (error) {
184  	    CFStringRef errorDescription = CFErrorCopyFailureReason(error);
185  	    kcm_log(1, "error retrieving entitlement: %ld, %s", (long)CFErrorGetCode(error), CFStringGetCStringPtr(errorDescription, kCFStringEncodingUTF8));
186  	    CFRELEASE_NULL(errorDescription);
187  	    CFRELEASE_NULL(error);
188  	}
189  	CFRELEASE_NULL(hasEntitlement);
190  	return false;
191      }
192      
193      CFRELEASE_NULL(hasEntitlement);
194      return true;
195      
196  }
197  
198  krb5_boolean
199  krb5_applesigned(krb5_context context, audit_token_t auditToken, const char *identifierToVerify)
200  {
201      bool applesigned = false;
202      OSStatus result = noErr;
203      CFDictionaryRef attributes = NULL;
204      SecCodeRef codeRef = NULL;
205      SecRequirementRef secRequirementRef = NULL;
206      CFStringRef requirement = NULL;
207      
208      requirement = CFStringCreateWithFormat(NULL, NULL, CFSTR("identifier \"%s\" and anchor apple"), identifierToVerify);
209      kcm_log(1, "requirement: %s", CFStringGetCStringPtr(requirement, kCFStringEncodingUTF8));
210      result = SecRequirementCreateWithString(requirement, kSecCSDefaultFlags, &secRequirementRef);
211      if (result || !secRequirementRef) {
212  	kcm_log(1, "Error creating requirement %d ", result);
213  	applesigned = false;
214  	goto cleanup;
215      }
216          
217      const void *keys[] = {
218  	kSecGuestAttributeAudit,
219      };
220      const void *values[] = {
221  	CFDataCreate(NULL, (const UInt8*)&auditToken, sizeof(auditToken)),
222      };
223      
224      attributes = CFDictionaryCreate(NULL, keys, values, 1,
225  						    &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
226      
227      result = SecCodeCopyGuestWithAttributes(NULL, attributes, kSecCSDefaultFlags, &codeRef);
228      if (result || !codeRef) {
229  	kcm_log(1, "Error creating code ref: %d ", result);
230  	applesigned = false;
231  	goto cleanup;
232      }
233      
234      result = SecCodeCheckValidity(codeRef, kSecCSDefaultFlags, secRequirementRef);
235      if (result) {
236  	kcm_log(1, "Error checking requirement: %d ", result);
237  	applesigned = false;
238  	goto cleanup;
239      }
240      
241      applesigned = true;
242      
243  cleanup:
244      
245      CFRELEASE_NULL(requirement);
246      CFRELEASE_NULL(secRequirementRef);
247      CFRELEASE_NULL(attributes);
248      CFRELEASE_NULL(codeRef);
249      
250      return applesigned;
251  }