/* * Copyright (c) 2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Portions of this software have been released under the following terms: * * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION * * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this file for any * purpose is hereby granted without fee, provided that the above * copyright notices and this notice appears in all source code copies, * and that none of the names of Open Software Foundation, Inc., Hewlett- * Packard Company or Digital Equipment Corporation be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Neither Open Software * Foundation, Inc., Hewlett-Packard Company nor Digital * Equipment Corporation makes any representations about the suitability * of this software for any purpose. * * Copyright (c) 2007, Novell, Inc. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Novell Inc. nor the names of its contributors * may be used to endorse or promote products derived from this * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @APPLE_LICENSE_HEADER_END@ */ /* ** ** NAME ** ** comauth.c ** ** FACILITY: ** ** Remote Procedure Call (RPC) ** ** ABSTRACT: ** ** Generic interface to authentication services ** ** */ #include <commonp.h> /* Common declarations for all RPC runtime */ #include <com.h> /* Common communications services */ #include <comp.h> /* Private communications services */ #include <comauth.h> /* Common Authentication services */ #if HAVE_SYS_KAUTH_H #include <sys/kauth.h> #endif #if HAVE_SYS_SYSCALL_H #include <sys/syscall.h> #endif /* * Internal variables to maintain the auth info cache. */ INTERNAL rpc_list_t auth_info_cache; INTERNAL rpc_mutex_t auth_info_cache_mutex; /* * R P C _ _ A U T H _ I N F O _ C A C H E _ L K U P */ INTERNAL rpc_auth_info_t *rpc__auth_info_cache_lkup ( unsigned_char_p_t /*server_princ_name*/, rpc_authn_level_t /*authn_level*/, rpc_auth_identity_handle_t /*auth_identity*/, rpc_authz_protocol_id_t /*authz_protocol*/, rpc_authn_protocol_id_t /* authn_protocol*/ ); /* * R P C _ _ A U T H _ I N F O _ C A C H E _ A D D */ INTERNAL void rpc__auth_info_cache_add ( rpc_auth_info_p_t /*auth_info*/ ); /* * R P C _ _ A U T H _ I N F O _ C A C H E _ R E M O V E */ INTERNAL void rpc__auth_info_cache_remove ( rpc_auth_info_p_t /*auth_info*/ ); /* * Macro to assign through a pointer iff the pointer is non-NULL. Note * that we depend on the fact that "val" is not evaluated if "ptr" is * NULL (i.e., this must be a macro, not a function). */ #define ASSIGN(ptr, val) \ ( \ (ptr) != NULL ? *(ptr) = (val) : 0 \ ) #define ASSIGN_COPY(buffer, length, val) do { \ char* _val = (char*) (val); \ size_t _vallength = _val ? strlen(_val) : 0; \ if ((buffer) == NULL || (length) < _vallength) { \ *st = rpc_s_ss_bad_buffer; \ return; \ } else { \ if (_val != NULL) \ memcpy((buffer), _val, _vallength + 1); \ else \ (buffer)[0] = '\0'; \ (length) = (unsigned32) _vallength; \ } \ } while (0) /* **++ ** ** ROUTINE NAME: rpc__auth_inq_supported ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Return a boolean indicating whether the authentication protocol ** is supported by the runtime. ** ** INPUTS: ** ** authn_prot_id Authentication protocol ID ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: boolean32 ** ** true if supported ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE boolean32 rpc__auth_inq_supported ( rpc_authn_protocol_id_t authn_prot_id ) { return (RPC_AUTHN_INQ_SUPPORTED(authn_prot_id)); } /* **++ ** ** ROUTINE NAME: rpc__auth_cvt_id_api_to_wire ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Return the wire value of an authentication protocol ID given its ** API counterpart. ** ** INPUTS: ** ** api_authn_prot_id API Authentication protocol ID ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: none ** ** status A value indicating the return status of the routine ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: unsigned32 ** ** The wire Authentication protocol ID. ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE unsigned32 rpc__auth_cvt_id_api_to_wire ( rpc_authn_protocol_id_t api_authn_prot_id, unsigned32 *status ) { if (! RPC_AUTHN_IN_RANGE(api_authn_prot_id) || ! RPC_AUTHN_INQ_SUPPORTED(api_authn_prot_id)) { *status = rpc_s_unknown_auth_protocol; return (0xeffaced); } *status = rpc_s_ok; return (rpc_g_authn_protocol_id[api_authn_prot_id].dce_rpc_authn_protocol_id); } /* **++ ** ** ROUTINE NAME: rpc__auth_cvt_id_wire_to_api ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Return the API value of an authentication protocol ID given its ** wire counterpart. ** ** INPUTS: ** ** wire_authn_prot_id Wire Authentication protocol ID ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: ** ** status A value indicating the return status of the routine ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: unsigned32 ** ** The API Authentication protocol ID. ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE rpc_authn_protocol_id_t rpc__auth_cvt_id_wire_to_api ( unsigned32 wire_authn_prot_id, unsigned32 *status ) { rpc_authn_protocol_id_t authn_protocol; for (authn_protocol = 0; authn_protocol < RPC_C_AUTHN_PROTOCOL_ID_MAX; authn_protocol++) { rpc_authn_protocol_id_elt_p_t aprot = &rpc_g_authn_protocol_id[authn_protocol]; if (aprot->epv != NULL && aprot->dce_rpc_authn_protocol_id == wire_authn_prot_id) { break; } } if (authn_protocol >= RPC_C_AUTHN_PROTOCOL_ID_MAX) { *status = rpc_s_unknown_auth_protocol; return ((rpc_authn_protocol_id_t)0xdeaddeadUL); } *status = rpc_s_ok; return (authn_protocol); } /* **++ ** ** ROUTINE NAME: rpc__auth_inq_rpc_prot_epv ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Return the RPC protocol specific entry point vector for a given ** Authentication protocol. ** ** INPUTS: ** ** authn_prot_id Authentication protocol ID ** rpc_prot_id RPC protocol ID ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: rpc_prot_epv_tbl ** ** The address of the RPC protocol specific, authentication ** protocol specific entry point vector. ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE rpc_auth_rpc_prot_epv_t *rpc__auth_rpc_prot_epv ( rpc_authn_protocol_id_t authn_prot_id, rpc_protocol_id_t rpc_prot_id ) { return (RPC_AUTHN_INQ_RPC_PROT_EPV(authn_prot_id,rpc_prot_id)); } /* **++ ** ** ROUTINE NAME: rpc__auth_info_reference ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Establish a reference to authentication info. ** ** INPUTS: ** ** auth_info Authentication information ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE void rpc__auth_info_reference ( rpc_auth_info_p_t auth_info ) { #ifdef DEBUG const char *info_type = auth_info->is_server?"server":"client"; #endif RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc__auth_info_reference) %p: bumping %s refcount (was %d, now %d)\n", auth_info, info_type, auth_info->refcount, auth_info->refcount + 1)); /* assert (auth_info->refcount >= 0); XXX unsigned values always >= 0*/ auth_info->refcount++; } /* **++ ** ** ROUTINE NAME: rpc__auth_info_binding_release ** ** SCOPE: PRIVATE - declared in comauth.h ** ** DESCRIPTION: ** ** Release reference to authentication info (stored in passed binding ** handle) previously returned by set_server or inq_caller. If we don't ** have any auth info, do nothing. ** ** INPUTS: ** ** binding_rep RPC binding handle ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE void rpc__auth_info_binding_release ( rpc_binding_rep_p_t binding_rep ) { rpc__auth_info_release (&binding_rep->auth_info); } /* **++ ** ** ROUTINE NAME: rpc__auth_info_release ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Release reference to authentication info previously returned by ** set_server or inq_caller. ** ** INPUTS: ** ** info Authentication info ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE void rpc__auth_info_release ( rpc_auth_info_p_t *info ) { rpc_auth_info_p_t auth_info = *info; const char *info_type; if (auth_info == NULL) { return; } info_type = auth_info->is_server?"server":"client"; RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc__auth_info_release) %p: dropping %s refcount (was %d, now %d)\n", auth_info, info_type, auth_info->refcount, auth_info->refcount-1 )); assert(auth_info->refcount >= 1); /* * Remove the reference. */ auth_info->refcount--; /* * Some special logic is required for cache maintenance on the * client side. */ if (!(auth_info->is_server)) { if (auth_info->refcount == 1) { /* * The auth info can be removed from the cache if there is only * one reference left to it. That single reference is the cache's * reference. */ rpc__auth_info_cache_remove (auth_info); } } /* * Free the auth info when nobody holds a reference to it. */ if (auth_info->refcount == 0) { (*rpc_g_authn_protocol_id[auth_info->authn_protocol].epv->free_info) (&auth_info); } /* * NULL out the caller's reference to the auth info. */ *info = NULL; } /* **++ ** ** ROUTINE NAME: rpc__key_info_reference ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Establish a reference to keyentication info. ** ** INPUTS: ** ** key_info Authentication information ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE void rpc__key_info_reference ( rpc_key_info_p_t key_info ) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc__key_info_reference) %p: bumping %s refcnt (was %d, now %d)\n", key_info, (key_info->is_server?"server":"client"), key_info->refcnt, key_info->refcnt + 1)); assert (key_info->refcnt >= 1); key_info->refcnt++; } /* **++ ** ** ROUTINE NAME: rpc__key_info_release ** ** SCOPE: PRIVATE - declared in com.h ** ** DESCRIPTION: ** ** Release reference to keyentication info previously returned by ** set_server or inq_caller. ** ** INPUTS: ** ** info Authentication info ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE void rpc__key_info_release ( rpc_key_info_p_t *info ) { rpc_key_info_p_t key_info = *info; if (key_info == NULL) { return; } *info = NULL; RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc__key_info_release) %p: dropping %s refcnt (was %d, now %d)\n", key_info, key_info->is_server?"server":"client", key_info->refcnt, key_info->refcnt-1 )); assert(key_info->refcnt >= 1); /* * Remove the reference. */ key_info->refcnt--; /* * Free the auth info when nobody holds a reference to it. */ if (key_info->refcnt == 0) { (*rpc_g_authn_protocol_id[key_info->auth_info->authn_protocol].epv->free_key) (&key_info); } } /* **++ ** ** ROUTINE NAME: rpc__auth_inq_my_princ_name ** ** SCOPE: PRIVATE - declared in comauth.h ** ** DESCRIPTION: ** ** ** ** ** ** INPUTS: ** ** h RPC binding handle ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: ** ** status A value indicating the return status of the routine ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE void rpc__auth_inq_my_princ_name ( unsigned32 dce_rpc_authn_protocol, unsigned32 princ_name_size, unsigned_char_p_t princ_name, unsigned32 *st ) { rpc_authn_protocol_id_t authn_protocol; authn_protocol = rpc__auth_cvt_id_wire_to_api(dce_rpc_authn_protocol, st); if (*st != rpc_s_ok) { return; } (*rpc_g_authn_protocol_id[authn_protocol] .epv->inq_my_princ_name) (princ_name_size, princ_name, st); } /* **++ ** ** ROUTINE NAME: rpc_binding_set_auth_info ** ** SCOPE: PUBLIC - declared in rpcauth.idl ** ** DESCRIPTION: ** ** Set up client handle for authentication. ** ** INPUTS: ** ** h RPC binding handle ** ** server_princ_name ** Name of server to authenticate to ** ** authn_level Authentication level ** ** authn_protocol Desired authentication protocol to use ** ** auth_identity Credentials to use on calls ** ** authz_protocol Authorization protocol to use ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: ** ** status A value indicating the return status of the routine ** rpc_s_invalid_binding ** RPC Protocol ID in binding handle was invalid. ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_binding_set_auth_info ( rpc_binding_handle_t binding_h, unsigned_char_p_t server_princ_name, unsigned32 authn_level, unsigned32 authn_protocol, rpc_auth_identity_handle_t auth_identity, unsigned32 authz_protocol, unsigned32 *st ) { rpc_auth_identity_handle_t ref_auth_identity; rpc_auth_info_p_t auth_info; rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; rpc_auth_epv_p_t auth_epv; boolean need_to_free_server_name = false; CODING_ERROR (st); RPC_VERIFY_INIT (); assert(binding_rep != NULL); RPC_BINDING_VALIDATE_CLIENT(binding_rep, st); if (*st != rpc_s_ok) return; /* * If asking to set to authentication type "none", just free any auth info * we have and return now. */ if (authn_protocol == rpc_c_authn_none) { rpc__auth_info_binding_release(binding_rep); return; } RPC_AUTHN_CHECK_SUPPORTED_RPC_PROT(authn_protocol, binding_rep->protocol_id, st); /* * If asking for default authn level, get the actual level now (i.e., * spare each auth service the effort of coding this logic). */ if (authn_level == rpc_c_authn_level_default) { rpc_mgmt_inq_dflt_authn_level (authn_protocol, &authn_level, st); if (*st != rpc_s_ok) return; } auth_epv = rpc_g_authn_protocol_id[authn_protocol].epv; /* * Resolve the auth_identity into a real reference to the identity * prior to the cache lookup. */ *st = (*auth_epv->resolve_id) (auth_identity, &ref_auth_identity); if (*st != rpc_s_ok) return; /* * If no server principal name was specified, go ask for it. * * Not all authentication protocols require a server principal * name to do authentication. Hence, only inquire the server * principal name if we know the authentication protocol is * secret key based. * * We did not move the inq_princ_name function to an * authentication service specific module because we need * a server principal name for the auth_info cache lookup. * * Note that we want to be avoid bypassing the auth_info * cache because certain protocol services cache credentials. * Allocating a new auth_info structure on every call can * cause these credentials to accumulate until the heap is * exhausted. */ if (server_princ_name == NULL) { switch (authn_protocol) { case rpc_c_authn_dce_secret: case rpc_c_authn_winnt: case rpc_c_authn_gss_negotiate: case rpc_c_authn_gss_mskrb: rpc_mgmt_inq_server_princ_name (binding_h, authn_protocol, &server_princ_name, st); if (*st != rpc_s_ok) return; need_to_free_server_name = true; break; default: break; } } /* * Consult the cache before creating a new auth info structure. * We may be able to add a reference to an already existing one. */ if ((auth_info = rpc__auth_info_cache_lkup (server_princ_name, authn_level, ref_auth_identity, authz_protocol, authn_protocol)) == NULL) { /* * A new auth info will have to be created. * Call authentication service to do generic (not specific * to a single RPC protocol) "set server" function. */ (*auth_epv->binding_set_auth_info) (server_princ_name, authn_level, auth_identity, authz_protocol, binding_h, &auth_info, st); if (*st != rpc_s_ok) { if (need_to_free_server_name) RPC_MEM_FREE (server_princ_name, RPC_C_MEM_STRING); return; } /* * Add this new auth info to a cache of auth infos. This cache * will be consulted on a subsequent call to this routine. */ rpc__auth_info_cache_add (auth_info); } /* * Release our reference to the identity. If a new auth_info was * created, then it added a reference also. */ (*auth_epv->release_id) (&ref_auth_identity); /* * If we inquired the server principal name, free it now. */ if (need_to_free_server_name) RPC_MEM_FREE (server_princ_name, RPC_C_MEM_STRING); /* * If we have any auth info for this binding already, lose it. */ if (binding_rep->auth_info != NULL) { rpc__auth_info_binding_release(binding_rep); } binding_rep->auth_info = auth_info; /* * Notify the protocol service that the binding has changed. */ (*rpc_g_protocol_id[binding_rep->protocol_id].binding_epv ->binding_changed) (binding_rep, st); } /* **++ ** ** ROUTINE NAME: rpc_binding_inq_auth_info ** ** SCOPE: PUBLIC - declared in rpcauth.idl ** ** DESCRIPTION: ** ** Return authentication and authorization information from a binding ** handle. ** ** INPUTS: ** ** h RPC binding handle ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: ** ** server_princ_name ** Name of server to authenticate to ** ** authn_level Authentication level ** ** authn_protocol Desired authentication protocol to use ** ** auth_identity Credentials to use on calls ** ** authz_protocol Authorization protocol to use ** ** status A value indicating the return status of the routine ** rpc_s_invalid_binding ** RPC Protocol ID in binding handle was invalid. ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_binding_inq_auth_info ( rpc_binding_handle_t binding_h, unsigned_char_p_t *server_princ_name, unsigned32 *authn_level, unsigned32 *authn_protocol, rpc_auth_identity_handle_t *auth_identity, unsigned32 *authz_protocol, unsigned32 *st ) { rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; rpc_auth_info_p_t auth_info; assert(binding_rep != NULL); CODING_ERROR (st); RPC_VERIFY_INIT (); RPC_BINDING_VALIDATE_CLIENT(binding_rep, st); if (*st != rpc_s_ok) return; auth_info = ((rpc_binding_rep_p_t)binding_h)->auth_info; if (auth_info == NULL) { *st = rpc_s_binding_has_no_auth; return; } assert(! auth_info->is_server); if (auth_info->server_princ_name == NULL) { (void) ASSIGN(server_princ_name, NULL); } else { (void) ASSIGN(server_princ_name, rpc_stralloc(auth_info->server_princ_name)); } (void) ASSIGN(authn_level, auth_info->authn_level); (void) ASSIGN(authn_protocol, auth_info->authn_protocol); (void) ASSIGN(auth_identity, auth_info->u.auth_identity); (void) ASSIGN(authz_protocol, auth_info->authz_protocol); *st = rpc_s_ok; } /* **++ ** ** ROUTINE NAME: rpc_server_register_auth_info ** ** SCOPE: PUBLIC - declared in rpcauth.idl ** ** DESCRIPTION: ** ** Register authentication information with the RPC runtime. ** ** INPUTS: ** ** authn_protocol Desired authentication protocol to use ** ** server_princ_name ** Name server should use ** ** get_key_func Function ptr to call to get keys ** ** arg Opaque params for key func. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** status A value indicating the return status of the routine ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_server_register_auth_info ( unsigned_char_p_t server_princ_name, unsigned32 authn_protocol, rpc_auth_key_retrieval_fn_t get_key_func, ndr_void_p_t arg, unsigned32 *st ) { CODING_ERROR (st); RPC_VERIFY_INIT (); if (authn_protocol == rpc_c_authn_none) { *st = rpc_s_ok; return; } if (authn_protocol == (typeof(authn_protocol))(rpc_c_authn_default) && get_key_func != NULL) { *st = rpc_s_key_func_not_allowed; return; } RPC_AUTHN_CHECK_SUPPORTED (authn_protocol, st); (*rpc_g_authn_protocol_id[authn_protocol] .epv->server_register_auth_info) (server_princ_name, get_key_func, (dce_pointer_t) arg, st); } /* **++ ** ** ROUTINE NAME: rpc_binding_inq_auth_client ** ** SCOPE: PUBLIC - declared in rpcauth.idl ** ** DESCRIPTION: ** ** Return authentication and authorization information from a binding ** handle to an authenticated client. ** be freed. ** ** INPUTS: ** ** binding_h Server-side binding handle to remote caller whose ** identity is being requested. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** privs PAC for remote caller. ** ** server_princ_name ** Server name that caller authenticated to. ** ** authn_level Authentication level used by remote caller. ** ** authn_protocol Authentication protocol used by remote caller. ** ** authz_protocol Authorization protocol used by remote caller. ** ** status A value indicating the return status of the routine ** rpc_s_invalid_binding ** RPC Protocol ID in binding handle was invalid. ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_binding_inq_auth_client ( rpc_binding_handle_t binding_h, rpc_authz_handle_t *privs, unsigned_char_p_t *server_princ_name, unsigned32 *authn_level, unsigned32 *authn_protocol, unsigned32 *authz_protocol, unsigned32 *st ) { rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; rpc_auth_info_p_t auth_info; assert(binding_rep != NULL); CODING_ERROR (st); RPC_VERIFY_INIT (); RPC_BINDING_VALIDATE_SERVER(binding_rep, st); if (*st != rpc_s_ok) return; auth_info = ((rpc_binding_rep_p_t)binding_h)->auth_info; if (auth_info == NULL) { *st = rpc_s_binding_has_no_auth; return; } assert(auth_info->is_server); (void) ASSIGN(privs, auth_info->u.s.privs); if (server_princ_name != NULL) { if (auth_info->server_princ_name == NULL) { (void) ASSIGN(server_princ_name, NULL); } else { (void) ASSIGN(server_princ_name, rpc_stralloc(auth_info->server_princ_name)); } } (void) ASSIGN(authn_level, auth_info->authn_level); (void) ASSIGN(authn_protocol, auth_info->authn_protocol); (void) ASSIGN(authz_protocol, auth_info->authz_protocol); *st = rpc_s_ok; } /* **++ ** ** ROUTINE NAME: rpc_binding_inq_auth_caller ** ** SCOPE: PUBLIC - declared in rpcauth.idl ** ** DESCRIPTION: ** ** Return authentication and 1.1+ authorization information from a binding ** handle to an authenticated client. ** ** INPUTS: ** ** binding_h Server-side binding handle to remote caller whose ** identity is being requested. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** creds Opaque handle on caller's credentials to ** be used in making sec_cred_ calls ** ** server_princ_name ** Server name that caller authenticated to. ** ** authn_level Authentication level used by remote caller. ** ** authn_protocol Authentication protocol used by remote caller. ** ** authz_protocol Authorization protocol used by remote caller. ** ** status A value indicating the return status of the routine ** rpc_s_invalid_binding ** RPC Protocol ID in binding handle was invalid. ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_binding_inq_auth_caller ( rpc_binding_handle_t binding_h, rpc_authz_cred_handle_t *creds, unsigned_char_p_t *server_princ_name, unsigned32 *authn_level, unsigned32 *authn_protocol, unsigned32 *authz_protocol, unsigned32 *st ) { rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; rpc_auth_info_p_t auth_info; assert(binding_rep != NULL); CODING_ERROR (st); RPC_VERIFY_INIT (); RPC_BINDING_VALIDATE_SERVER(binding_rep, st); if (*st != rpc_s_ok) return; auth_info = ((rpc_binding_rep_p_t)binding_h)->auth_info; if (auth_info == NULL) { *st = rpc_s_binding_has_no_auth; return; } assert(auth_info->is_server); if (auth_info->u.s.creds != NULL) { *creds = *auth_info->u.s.creds; } if (server_princ_name != NULL) { if (auth_info->server_princ_name == NULL) { (void) ASSIGN(server_princ_name, NULL); } else { (void) ASSIGN(server_princ_name, rpc_stralloc(auth_info->server_princ_name)); } } (void) ASSIGN(authn_level, auth_info->authn_level); (void) ASSIGN(authn_protocol, auth_info->authn_protocol); (void) ASSIGN(authz_protocol, auth_info->authz_protocol); *st = rpc_s_ok; } /* **++ ** ** ROUTINE NAME: rpc_mgmt_inq_dflt_protect_level ** ** SCOPE: PUBLIC - declared in rpcauth.idl ** ** DESCRIPTION: ** ** Returns the default authentication level for an authentication service. ** ** INPUTS: ** ** authn_protocol Desired authentication protocol. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** authn_level Authentication level used by remote caller. ** ** status A value indicating the return status of the routine ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_mgmt_inq_dflt_protect_level ( unsigned32 authn_protocol, unsigned32 *authn_level, unsigned32 *st ) { CODING_ERROR (st); RPC_VERIFY_INIT (); if (authn_protocol == rpc_c_authn_none) { *authn_level = rpc_c_authn_level_none; *st = rpc_s_ok; return; } RPC_AUTHN_CHECK_SUPPORTED (authn_protocol, st); (*rpc_g_authn_protocol_id[authn_protocol] .epv->mgmt_inq_dflt_auth_level) (authn_level, st); } /* * Retain entry point with old name for compatibility. */ PUBLIC void rpc_mgmt_inq_dflt_authn_level ( unsigned32 authn_protocol, unsigned32 *authn_level, unsigned32 *st ) { rpc_mgmt_inq_dflt_protect_level (authn_protocol, authn_level, st); } /* **++ ** ** ROUTINE NAME: rpc__auth_info_cache_init ** ** SCOPE: PRIVATE - declared in comauth.h ** ** DESCRIPTION: ** ** Initialize the auth info cache including mutexes and list head. ** ** INPUTS: none ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** status A value indicating the return status of the routine ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PRIVATE void rpc__auth_info_cache_init ( unsigned32 *status ) { CODING_ERROR (status); RPC_MUTEX_INIT (auth_info_cache_mutex); RPC_LIST_INIT (auth_info_cache); *status = rpc_s_ok; } /* **++ ** ** ROUTINE NAME: rpc__auth_info_cache_lkup ** ** SCOPE: INTERNAL - declared locally ** ** DESCRIPTION: ** ** Scan a linked list of auth info structures looking for one which ** contains fields which match the input parameters. If and when a ** match is found the reference count of the auth info structure will ** be incremented before being returned. ** ** Note that it is possible for a null server principal name to ** match an auth info structure if that structure also has a ** null server principal name. ** ** INPUTS: ** ** server_princ_name Server principal name. ** authn_level Authentication level. ** authn_identity Authentication identity handle. ** authz_protocol Authorization protocol. ** authn_protocol Authentication protocol. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: rpc_auth_info_t * ** ** A pointer to a matching auth info, NULL if none found. ** ** SIDE EFFECTS: ** ** If a matching auth info is found the reference count of it ** will have already been incremented. ** **-- **/ INTERNAL rpc_auth_info_t *rpc__auth_info_cache_lkup ( unsigned_char_p_t server_princ_name, rpc_authn_level_t authn_level, rpc_auth_identity_handle_t auth_identity, rpc_authz_protocol_id_t authz_protocol, rpc_authn_protocol_id_t authn_protocol ) { rpc_auth_info_t *auth_info; RPC_MUTEX_LOCK (auth_info_cache_mutex); /* * Scan the linked list of auth info structure looking for one * whose fields match the input args. */ RPC_LIST_FIRST (auth_info_cache, auth_info, rpc_auth_info_p_t); while (auth_info != NULL) { if (( /* * DCE secret key authentication requires a * non-null server principal name for authentication. * We allow a null server principal name here so * that this will work in the future when an * authentication service without this requirement * is used. */ ((server_princ_name == NULL) && (auth_info->server_princ_name == NULL)) || (server_princ_name && auth_info->server_princ_name && (strcmp ((char *) server_princ_name, (char *) auth_info->server_princ_name) == 0)) ) && (authn_level == auth_info->authn_level) && (authn_protocol == auth_info->authn_protocol) && (authz_protocol == auth_info->authz_protocol) && (auth_identity == auth_info->u.auth_identity)) { /* * A matching auth info was found. */ rpc__auth_info_reference (auth_info); break; } RPC_LIST_NEXT (auth_info, auth_info, rpc_auth_info_p_t); } RPC_MUTEX_UNLOCK (auth_info_cache_mutex); return (auth_info); } /* **++ ** ** ROUTINE NAME: rpc__auth_info_cache_add ** ** SCOPE: INTERNAL - declared locally ** ** DESCRIPTION: ** ** Add an auth info structure to a linked list of them. The ** reference count of the added auth info structure will be incremented ** by the caller to account for its presence in the cache. ** ** INPUTS: ** ** auth_info The auth info to be added. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** ** The reference count of the added auth info structure will be ** incremented. ** **-- **/ INTERNAL void rpc__auth_info_cache_add ( rpc_auth_info_p_t auth_info ) { assert (!auth_info->is_server); RPC_MUTEX_LOCK (auth_info_cache_mutex); RPC_LIST_ADD_HEAD (auth_info_cache, auth_info, rpc_auth_info_p_t); rpc__auth_info_reference (auth_info); RPC_MUTEX_UNLOCK (auth_info_cache_mutex); } /* **++ ** ** ROUTINE NAME: rpc__auth_info_cache_remove ** ** SCOPE: INTERNAL - declared locally ** ** DESCRIPTION: ** ** Remove an auth info structure from a linked list of them. The ** cache's reference to the auth info structure will be released by ** the caller. ** ** It is assumed that the caller has already determined it is OK to ** remove this auth info from the cache. It is expected that the ** cache holds the last reference to this auth info structure at ** this point. ** ** INPUTS: ** ** auth_info The auth info to be removed. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: none ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ INTERNAL void rpc__auth_info_cache_remove ( rpc_auth_info_p_t auth_info ) { assert (!auth_info->is_server); RPC_MUTEX_LOCK (auth_info_cache_mutex); /* * Make sure, under the protection of the cache lock, that this * really should be removed from the cache. */ if (auth_info->refcount == 1) { const char *info_type; RPC_LIST_REMOVE (auth_info_cache, auth_info); info_type = auth_info->is_server?"server":"client"; RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc__auth_info_release) %p: dropping %s refcount (was %d, now %d)\n", auth_info, info_type, auth_info->refcount, auth_info->refcount-1 )); assert(auth_info->refcount >= 1); auth_info->refcount--; } RPC_MUTEX_UNLOCK (auth_info_cache_mutex); } /* **++ ** ** ROUTINE NAME: rpc_server_inq_call_attributes ** ** SCOPE: PUBLIC - declared in rpcauth.idl ** ** DESCRIPTION: ** ** Return RPC call attributes ** ** INPUTS: ** ** binding_h Server-side binding handle to remote caller whose ** identity is being requested. ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** attributes RPC call attributes ** ** status Status code ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_server_inq_call_attributes ( rpc_binding_handle_t binding_h, rpc_call_attributes_t *attributes, unsigned32 *st ) { rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; rpc_auth_info_p_t auth_info; rpc_call_attributes_v1_t *v1_attributes; assert(binding_rep != NULL); CODING_ERROR (st); RPC_VERIFY_INIT (); RPC_BINDING_VALIDATE_SERVER(binding_rep, st); if (*st != rpc_s_ok) return; auth_info = ((rpc_binding_rep_p_t)binding_h)->auth_info; if (auth_info == NULL) { *st = rpc_s_binding_has_no_auth; return; } assert(auth_info->is_server); if (attributes->version != 1) { *st = rpc_s_auth_badversion; return; } v1_attributes = (rpc_call_attributes_v1_p_t)attributes; if (v1_attributes->flags & rpc_query_server_principal_name) { ASSIGN_COPY(v1_attributes->server_princ_name, v1_attributes->server_princ_name_buff_len, auth_info->server_princ_name); } if (v1_attributes->flags & rpc_query_client_principal_name) { if (auth_info->authz_protocol != rpc_c_authz_name) { *st = rpc_s_binding_has_no_auth; return; } ASSIGN_COPY(v1_attributes->client_princ_name, v1_attributes->client_princ_name_buff_len, (unsigned_char_p_t)auth_info->u.s.privs); } v1_attributes->authn_level = auth_info ? auth_info->authn_level : rpc_c_protect_level_none; v1_attributes->authn_protocol = auth_info ? auth_info->authn_protocol : rpc_c_authn_winnt; v1_attributes->null_session = /* FIXME: determine meaning of this flag */ 0; *st = rpc_s_ok; } /* **++ ** ** ROUTINE NAME: rpc_binding_inq_security_context ** ** SCOPE: PUBLIC - declared in rpc.idl ** ** DESCRIPTION: ** ** Return mechanism-specific security context from a binding handle. ** ** INPUTS: ** ** h RPC binding handle ** ** INPUTS/OUTPUTS: ** ** OUTPUTS: ** ** authn_protocol Authentication level ** ** sec_context Security context ** ** status A value indicating the return status of the routine ** rpc_s_invalid_binding ** RPC Protocol ID in binding handle was invalid. ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: none ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_binding_inq_security_context ( rpc_binding_handle_t binding_h, unsigned32 *authn_protocol, void **mech_context, unsigned32 *st ) { rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; rpc_auth_info_p_t auth_info; CODING_ERROR (st); RPC_VERIFY_INIT (); *authn_protocol = rpc_c_authn_none; *mech_context = NULL; auth_info = binding_rep->auth_info; if (auth_info == NULL) { *st = rpc_s_binding_has_no_auth; return; } *authn_protocol = auth_info->authn_protocol; if (*authn_protocol == rpc_c_authn_none) { *st = rpc_s_ok; return; } RPC_AUTHN_CHECK_SUPPORTED (*authn_protocol, st); if (rpc_g_authn_protocol_id[*authn_protocol] .epv->inq_sec_context == NULL) { *st = rpc_s_binding_has_no_auth; return; } (*rpc_g_authn_protocol_id[*authn_protocol] .epv->inq_sec_context) (auth_info, mech_context, st); } /* **++ ** ** ROUTINE NAME: rpc_impersonate_named_pipe_client ** ** SCOPE: PUBLIC - declared in rpc.idl ** ** DESCRIPTION: ** ** This routine allows a server thread to run with the security credentials ** of the active client. ** ** INPUTS: ** ** binding_h RPC binding handle ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** status A value indicating the status of the routine. ** ** rpc_s_ok The call was successful. ** rpc_s_invalid_binding RPC Protocol ID in binding handle ** was invalid. ** rpc_s_coding_error ** rpc_s_protocol_error Wrong type of connection ** rpc_s_wrong_kind_of_binding Wrong kind of binding ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: void ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_impersonate_named_pipe_client ( rpc_binding_handle_t binding_h, unsigned32 *status ) { #if HAVE_PTHREAD_SETUGID_NP rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; rpc_protocol_id_t protid; rpc_prot_network_epv_p_t net_epv; uid_t euid = 0; gid_t egid = 0; int ret; assert(binding_rep != NULL); CODING_ERROR (status); RPC_VERIFY_INIT (); RPC_BINDING_VALIDATE(binding_rep, status); if (*status != rpc_s_ok) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_impersonate_named_pipe_client): invalid binding\n")); return; } if (RPC_BINDING_IS_CLIENT (binding_rep)) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_impersonate_named_pipe_client): wrong type of binding\n")); *status = rpc_s_wrong_kind_of_binding; return; } protid = binding_rep->protocol_id; net_epv = RPC_PROTOCOL_INQ_NETWORK_EPV (protid); if (net_epv->network_getpeereid == NULL) { *status = rpc_s_protocol_error; return; } /* * Call network protocol routine. */ (*net_epv->network_getpeereid) (binding_rep, &euid, &egid, status); if (*status != rpc_s_ok) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_impersonate_named_pipe_client): network_getpeereid failed %d\n", *status)); return; } ret = pthread_setugid_np(euid, egid); if (ret != 0) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_impersonate_named_pipe_client): pthread_setugid_np failed %d for euid %d, egid %d\n", errno, euid, egid)); *status = rpc_s_no_context_available; return; } /* opt back into the dynamic group resolutions. For better performance, we only add in our egid instead of the entire group list. */ ret = syscall(SYS_initgroups, 1, &egid, euid); if (ret == -1) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_impersonate_named_pipe_client): SYS_initgroups failed %d for euid %d, egid %d\n", errno, euid, egid)); *status = rpc_s_no_context_available; goto error; } *status = rpc_s_ok; error: if (*status != rpc_s_ok) { /* error occurred, try to revert back to previous user/group ID */ RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_impersonate_named_pipe_client): reverting credentials due to error %d\n", *status)); pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE); } #else *status = rpc_s_not_supported; #endif } /* **++ ** ** ROUTINE NAME: rpc_revert_to_self ** ** SCOPE: PUBLIC - declared in rpc.idl ** ** DESCRIPTION: ** ** This routine allows a server thread to end impersonation and revert to the ** per process credentials. ** ** INPUTS: none ** ** INPUTS/OUTPUTS: none ** ** OUTPUTS: ** ** status A value indicating the status of the routine. ** ** rpc_s_ok The call was successful. ** rpc_s_invalid_binding RPC Protocol ID in binding handle ** was invalid. ** rpc_s_coding_error ** rpc_s_protocol_error Wrong type of connection ** rpc_s_wrong_kind_of_binding Wrong kind of binding ** ** IMPLICIT INPUTS: none ** ** IMPLICIT OUTPUTS: none ** ** FUNCTION VALUE: void ** ** SIDE EFFECTS: none ** **-- **/ PUBLIC void rpc_revert_to_self ( rpc_binding_handle_t binding_h, unsigned32 *status ) { #if HAVE_PTHREAD_SETUGID_NP rpc_binding_rep_p_t binding_rep = (rpc_binding_rep_p_t) binding_h; int ret; assert(binding_rep != NULL); CODING_ERROR (status); RPC_VERIFY_INIT (); RPC_BINDING_VALIDATE(binding_rep, status); if (*status != rpc_s_ok) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_revert_to_self): invalid binding\n")); return; } if (RPC_BINDING_IS_CLIENT (binding_rep)) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_revert_to_self): wrong type of binding\n")); *status = rpc_s_wrong_kind_of_binding; return; } ret = pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE); if (ret != 0) { RPC_DBG_PRINTF(rpc_e_dbg_auth, 3, ("(rpc_revert_to_self): pthread_setugid_np failed %d\n", errno)); *status = rpc_s_no_context_available; return; } *status = rpc_s_ok; #else *status = rpc_s_not_supported; #endif }