
We turn on a few extra warnings and fix the fallout that occurs when building with --enable-developer. Note that we get different warnings on different machines and so this will be a work in progress. So far, we have built on NetBSD/amd64 5.99.64 (which uses gcc 4.5.3) and Ubuntu 10.04.3 LTS (which uses gcc 4.4.3). Notably, we fixed 1. a lot of missing structure initialisers, 2. unchecked return values for functions that glibc marks as __attribute__((warn-unused-result)), 3. made minor modifications to slc and asn1_compile which can generate code which generates warnings, and 4. a few stragglers here and there. We turned off the extended warnings for many programs in appl/ as they are nearing the end of their useful lifetime, e.g. rsh, rcp, popper, ftp and telnet. Interestingly, glibc's strncmp() macro needed to be worked around whereas the function calls did not. We have not yet tried this on 32 bit platforms, so there will be a few more warnings when we do.
1813 lines
37 KiB
C
1813 lines
37 KiB
C
/*
|
|
* Copyright (c) 2005, PADL Software Pty Ltd.
|
|
* All rights reserved.
|
|
*
|
|
* Portions Copyright (c) 2009 Apple 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE 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.
|
|
*/
|
|
|
|
#include "kcm_locl.h"
|
|
#include <heimntlm.h>
|
|
|
|
static void
|
|
kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name);
|
|
|
|
|
|
int
|
|
kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session)
|
|
{
|
|
#if 0 /* XXX pppd is running in diffrent session the user */
|
|
if (session != -1)
|
|
return (client->session == session);
|
|
else
|
|
#endif
|
|
return (client->uid == uid);
|
|
}
|
|
|
|
static krb5_error_code
|
|
kcm_op_noop(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
KCM_LOG_REQUEST(context, client, opcode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* Response:
|
|
* NameZ
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_get_name(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
|
|
{
|
|
krb5_error_code ret;
|
|
char *name = NULL;
|
|
kcm_ccache ccache;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = krb5_store_stringz(response, ccache->name);
|
|
if (ret) {
|
|
kcm_release_ccache(context, ccache);
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
free(name);
|
|
kcm_release_ccache(context, ccache);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
*
|
|
* Response:
|
|
* NameZ
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_gen_new(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
char *name;
|
|
|
|
KCM_LOG_REQUEST(context, client, opcode);
|
|
|
|
name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
|
|
if (name == NULL) {
|
|
return KRB5_CC_NOMEM;
|
|
}
|
|
|
|
ret = krb5_store_stringz(response, name);
|
|
free(name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* Principal
|
|
*
|
|
* Response:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_initialize(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
kcm_ccache ccache;
|
|
krb5_principal principal;
|
|
krb5_error_code ret;
|
|
char *name;
|
|
#if 0
|
|
kcm_event event;
|
|
#endif
|
|
|
|
KCM_LOG_REQUEST(context, client, opcode);
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = krb5_ret_principal(request, &principal);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_new_client(context, client, name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
krb5_free_principal(context, principal);
|
|
return ret;
|
|
}
|
|
|
|
ccache->client = principal;
|
|
|
|
free(name);
|
|
|
|
#if 0
|
|
/*
|
|
* Create a new credentials cache. To mitigate DoS attacks we will
|
|
* expire it in 30 minutes unless it has some credentials added
|
|
* to it
|
|
*/
|
|
|
|
event.fire_time = 30 * 60;
|
|
event.expire_time = 0;
|
|
event.backoff_time = 0;
|
|
event.action = KCM_EVENT_DESTROY_EMPTY_CACHE;
|
|
event.ccache = ccache;
|
|
|
|
ret = kcm_enqueue_event_relative(context, &event);
|
|
#endif
|
|
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
*
|
|
* Response:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_destroy(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = kcm_ccache_destroy_client(context, client, name);
|
|
if (ret == 0)
|
|
kcm_drop_default_cache(context, client, name);
|
|
|
|
free(name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* Creds
|
|
*
|
|
* Response:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_store(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_creds creds;
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_creds(request, &creds);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
krb5_free_cred_contents(context, &creds);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
|
|
if (ret) {
|
|
free(name);
|
|
krb5_free_cred_contents(context, &creds);
|
|
kcm_release_ccache(context, ccache);
|
|
return ret;
|
|
}
|
|
|
|
kcm_ccache_enqueue_default(context, ccache, &creds);
|
|
|
|
free(name);
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* WhichFields
|
|
* MatchCreds
|
|
*
|
|
* Response:
|
|
* Creds
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_retrieve(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
uint32_t flags;
|
|
krb5_creds mcreds;
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
krb5_creds *credp;
|
|
int free_creds = 0;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_uint32(request, &flags);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = krb5_ret_creds_tag(request, &mcreds);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
if (disallow_getting_krbtgt &&
|
|
mcreds.server->name.name_string.len == 2 &&
|
|
strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0)
|
|
{
|
|
free(name);
|
|
krb5_free_cred_contents(context, &mcreds);
|
|
return KRB5_FCC_PERM;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
krb5_free_cred_contents(context, &mcreds);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_retrieve_cred(context, ccache, flags,
|
|
&mcreds, &credp);
|
|
if (ret && ((flags & KRB5_GC_CACHED) == 0) &&
|
|
!krb5_is_config_principal(context, mcreds.server)) {
|
|
krb5_ccache_data ccdata;
|
|
|
|
/* try and acquire */
|
|
HEIMDAL_MUTEX_lock(&ccache->mutex);
|
|
|
|
/* Fake up an internal ccache */
|
|
kcm_internal_ccache(context, ccache, &ccdata);
|
|
|
|
/* glue cc layer will store creds */
|
|
ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp);
|
|
if (ret == 0)
|
|
free_creds = 1;
|
|
|
|
HEIMDAL_MUTEX_unlock(&ccache->mutex);
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ret = krb5_store_creds(response, credp);
|
|
}
|
|
|
|
free(name);
|
|
krb5_free_cred_contents(context, &mcreds);
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
if (free_creds)
|
|
krb5_free_cred_contents(context, credp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
*
|
|
* Response:
|
|
* Principal
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_get_principal(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
if (ccache->client == NULL)
|
|
ret = KRB5_CC_NOTFOUND;
|
|
else
|
|
ret = krb5_store_principal(response, ccache->client);
|
|
|
|
free(name);
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
*
|
|
* Response:
|
|
* UUIDs
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_get_cred_uuid_list(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_creds *creds;
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
free(name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (creds = ccache->creds ; creds ; creds = creds->next) {
|
|
ssize_t sret;
|
|
sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
|
|
if (sret != sizeof(creds->uuid)) {
|
|
ret = ENOMEM;
|
|
break;
|
|
}
|
|
}
|
|
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* Cursor
|
|
*
|
|
* Response:
|
|
* Creds
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_get_cred_by_uuid(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
struct kcm_creds *c;
|
|
kcmuuid_t uuid;
|
|
ssize_t sret;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
free(name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sret = krb5_storage_read(request, &uuid, sizeof(uuid));
|
|
if (sret != sizeof(uuid)) {
|
|
kcm_release_ccache(context, ccache);
|
|
krb5_clear_error_message(context);
|
|
return KRB5_CC_IO;
|
|
}
|
|
|
|
c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
|
|
if (c == NULL) {
|
|
kcm_release_ccache(context, ccache);
|
|
return KRB5_CC_END;
|
|
}
|
|
|
|
HEIMDAL_MUTEX_lock(&ccache->mutex);
|
|
ret = krb5_store_creds(response, &c->cred);
|
|
HEIMDAL_MUTEX_unlock(&ccache->mutex);
|
|
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* WhichFields
|
|
* MatchCreds
|
|
*
|
|
* Response:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_remove_cred(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
uint32_t whichfields;
|
|
krb5_creds mcreds;
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_uint32(request, &whichfields);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = krb5_ret_creds_tag(request, &mcreds);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
krb5_free_cred_contents(context, &mcreds);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
|
|
|
|
/* XXX need to remove any events that match */
|
|
|
|
free(name);
|
|
krb5_free_cred_contents(context, &mcreds);
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* Flags
|
|
*
|
|
* Response:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_set_flags(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
uint32_t flags;
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_uint32(request, &flags);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
/* we don't really support any flags yet */
|
|
free(name);
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* UID
|
|
* GID
|
|
*
|
|
* Response:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_chown(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
uint32_t uid;
|
|
uint32_t gid;
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_uint32(request, &uid);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = krb5_ret_uint32(request, &gid);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_chown(context, client, ccache, uid, gid);
|
|
|
|
free(name);
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* Mode
|
|
*
|
|
* Response:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_chmod(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
uint16_t mode;
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_uint16(request, &mode);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_chmod(context, client, ccache, mode);
|
|
|
|
free(name);
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Protocol extensions for moving ticket acquisition responsibility
|
|
* from client to KCM follow.
|
|
*/
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* ServerPrincipalPresent
|
|
* ServerPrincipal OPTIONAL
|
|
* Key
|
|
*
|
|
* Repsonse:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_get_initial_ticket(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
int8_t not_tgt = 0;
|
|
krb5_principal server = NULL;
|
|
krb5_keyblock key;
|
|
|
|
krb5_keyblock_zero(&key);
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_int8(request, ¬_tgt);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
if (not_tgt) {
|
|
ret = krb5_ret_principal(request, &server);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = krb5_ret_keyblock(request, &key);
|
|
if (ret) {
|
|
free(name);
|
|
if (server != NULL)
|
|
krb5_free_principal(context, server);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret == 0) {
|
|
HEIMDAL_MUTEX_lock(&ccache->mutex);
|
|
|
|
if (ccache->server != NULL) {
|
|
krb5_free_principal(context, ccache->server);
|
|
ccache->server = NULL;
|
|
}
|
|
|
|
krb5_free_keyblock(context, &ccache->key.keyblock);
|
|
|
|
ccache->server = server;
|
|
ccache->key.keyblock = key;
|
|
ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
|
|
|
|
ret = kcm_ccache_enqueue_default(context, ccache, NULL);
|
|
if (ret) {
|
|
ccache->server = NULL;
|
|
krb5_keyblock_zero(&ccache->key.keyblock);
|
|
ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
|
|
}
|
|
|
|
HEIMDAL_MUTEX_unlock(&ccache->mutex);
|
|
}
|
|
|
|
free(name);
|
|
|
|
if (ret != 0) {
|
|
krb5_free_principal(context, server);
|
|
krb5_free_keyblock(context, &key);
|
|
}
|
|
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* NameZ
|
|
* ServerPrincipal
|
|
* KDCFlags
|
|
* EncryptionType
|
|
*
|
|
* Repsonse:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_get_ticket(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
krb5_principal server = NULL;
|
|
krb5_ccache_data ccdata;
|
|
krb5_creds in, *out;
|
|
krb5_kdc_flags flags;
|
|
|
|
memset(&in, 0, sizeof(in));
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_uint32(request, &flags.i);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = krb5_ret_int32(request, &in.session.keytype);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = krb5_ret_principal(request, &server);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode,
|
|
name, &ccache);
|
|
if (ret) {
|
|
krb5_free_principal(context, server);
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
HEIMDAL_MUTEX_lock(&ccache->mutex);
|
|
|
|
/* Fake up an internal ccache */
|
|
kcm_internal_ccache(context, ccache, &ccdata);
|
|
|
|
in.client = ccache->client;
|
|
in.server = server;
|
|
in.times.endtime = 0;
|
|
|
|
/* glue cc layer will store creds */
|
|
ret = krb5_get_credentials_with_flags(context, 0, flags,
|
|
&ccdata, &in, &out);
|
|
|
|
HEIMDAL_MUTEX_unlock(&ccache->mutex);
|
|
|
|
krb5_free_principal(context, server);
|
|
|
|
if (ret == 0)
|
|
krb5_free_cred_contents(context, out);
|
|
|
|
kcm_release_ccache(context, ccache);
|
|
free(name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Request:
|
|
* OldNameZ
|
|
* NewNameZ
|
|
*
|
|
* Repsonse:
|
|
*
|
|
*/
|
|
static krb5_error_code
|
|
kcm_op_move_cache(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_ccache oldid, newid;
|
|
char *oldname, *newname;
|
|
|
|
ret = krb5_ret_stringz(request, &oldname);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
|
|
|
|
ret = krb5_ret_stringz(request, &newname);
|
|
if (ret) {
|
|
free(oldname);
|
|
return ret;
|
|
}
|
|
|
|
/* move to ourself is simple, done! */
|
|
if (strcmp(oldname, newname) == 0) {
|
|
free(oldname);
|
|
free(newname);
|
|
return 0;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
|
|
if (ret) {
|
|
free(oldname);
|
|
free(newname);
|
|
return ret;
|
|
}
|
|
|
|
/* Check if new credential cache exists, if not create one. */
|
|
ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
|
|
if (ret == KRB5_FCC_NOFILE)
|
|
ret = kcm_ccache_new_client(context, client, newname, &newid);
|
|
free(newname);
|
|
|
|
if (ret) {
|
|
free(oldname);
|
|
kcm_release_ccache(context, oldid);
|
|
return ret;
|
|
}
|
|
|
|
HEIMDAL_MUTEX_lock(&oldid->mutex);
|
|
HEIMDAL_MUTEX_lock(&newid->mutex);
|
|
|
|
/* move content */
|
|
{
|
|
kcm_ccache_data tmp;
|
|
|
|
#define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
|
|
|
|
MOVE(newid, oldid, flags);
|
|
MOVE(newid, oldid, client);
|
|
MOVE(newid, oldid, server);
|
|
MOVE(newid, oldid, creds);
|
|
MOVE(newid, oldid, tkt_life);
|
|
MOVE(newid, oldid, renew_life);
|
|
MOVE(newid, oldid, key);
|
|
MOVE(newid, oldid, kdc_offset);
|
|
#undef MOVE
|
|
}
|
|
|
|
HEIMDAL_MUTEX_unlock(&oldid->mutex);
|
|
HEIMDAL_MUTEX_unlock(&newid->mutex);
|
|
|
|
kcm_release_ccache(context, oldid);
|
|
kcm_release_ccache(context, newid);
|
|
|
|
ret = kcm_ccache_destroy_client(context, client, oldname);
|
|
if (ret == 0)
|
|
kcm_drop_default_cache(context, client, oldname);
|
|
|
|
free(oldname);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static krb5_error_code
|
|
kcm_op_get_cache_uuid_list(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
KCM_LOG_REQUEST(context, client, opcode);
|
|
|
|
return kcm_ccache_get_uuids(context, client, opcode, response);
|
|
}
|
|
|
|
static krb5_error_code
|
|
kcm_op_get_cache_by_uuid(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcmuuid_t uuid;
|
|
ssize_t sret;
|
|
kcm_ccache cache;
|
|
|
|
KCM_LOG_REQUEST(context, client, opcode);
|
|
|
|
sret = krb5_storage_read(request, &uuid, sizeof(uuid));
|
|
if (sret != sizeof(uuid)) {
|
|
krb5_clear_error_message(context);
|
|
return KRB5_CC_IO;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = kcm_access(context, client, opcode, cache);
|
|
if (ret)
|
|
ret = KRB5_FCC_NOFILE;
|
|
|
|
if (ret == 0)
|
|
ret = krb5_store_stringz(response, cache->name);
|
|
|
|
kcm_release_ccache(context, cache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct kcm_default_cache *default_caches;
|
|
|
|
static krb5_error_code
|
|
kcm_op_get_default_cache(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_default_cache *c;
|
|
krb5_error_code ret;
|
|
const char *name = NULL;
|
|
char *n = NULL;
|
|
int aret;
|
|
|
|
KCM_LOG_REQUEST(context, client, opcode);
|
|
|
|
for (c = default_caches; c != NULL; c = c->next) {
|
|
if (kcm_is_same_session(client, c->uid, c->session)) {
|
|
name = c->name;
|
|
break;
|
|
}
|
|
}
|
|
if (name == NULL)
|
|
name = n = kcm_ccache_first_name(client);
|
|
|
|
if (name == NULL) {
|
|
aret = asprintf(&n, "%d", (int)client->uid);
|
|
if (aret != -1)
|
|
name = n;
|
|
}
|
|
if (name == NULL)
|
|
return ENOMEM;
|
|
ret = krb5_store_stringz(response, name);
|
|
if (n)
|
|
free(n);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
|
|
{
|
|
struct kcm_default_cache **c;
|
|
|
|
for (c = &default_caches; *c != NULL; c = &(*c)->next) {
|
|
if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
|
|
continue;
|
|
if (strcmp((*c)->name, name) == 0) {
|
|
struct kcm_default_cache *h = *c;
|
|
*c = (*c)->next;
|
|
free(h->name);
|
|
free(h);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static krb5_error_code
|
|
kcm_op_set_default_cache(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_default_cache *c;
|
|
krb5_error_code ret;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
for (c = default_caches; c != NULL; c = c->next) {
|
|
if (kcm_is_same_session(client, c->uid, c->session))
|
|
break;
|
|
}
|
|
if (c == NULL) {
|
|
c = malloc(sizeof(*c));
|
|
if (c == NULL)
|
|
return ENOMEM;
|
|
c->session = client->session;
|
|
c->uid = client->uid;
|
|
c->name = strdup(name);
|
|
|
|
c->next = default_caches;
|
|
default_caches = c;
|
|
} else {
|
|
free(c->name);
|
|
c->name = strdup(name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static krb5_error_code
|
|
kcm_op_get_kdc_offset(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
|
|
free(name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
HEIMDAL_MUTEX_lock(&ccache->mutex);
|
|
ret = krb5_store_int32(response, ccache->kdc_offset);
|
|
HEIMDAL_MUTEX_unlock(&ccache->mutex);
|
|
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static krb5_error_code
|
|
kcm_op_set_kdc_offset(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_ccache ccache;
|
|
int32_t offset;
|
|
char *name;
|
|
|
|
ret = krb5_ret_stringz(request, &name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
KCM_LOG_REQUEST_NAME(context, client, opcode, name);
|
|
|
|
ret = krb5_ret_int32(request, &offset);
|
|
if (ret) {
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
|
|
free(name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
HEIMDAL_MUTEX_lock(&ccache->mutex);
|
|
ccache->kdc_offset = offset;
|
|
HEIMDAL_MUTEX_unlock(&ccache->mutex);
|
|
|
|
kcm_release_ccache(context, ccache);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct kcm_ntlm_cred {
|
|
kcmuuid_t uuid;
|
|
char *user;
|
|
char *domain;
|
|
krb5_data nthash;
|
|
uid_t uid;
|
|
pid_t session;
|
|
struct kcm_ntlm_cred *next;
|
|
};
|
|
|
|
static struct kcm_ntlm_cred *ntlm_head;
|
|
|
|
static void
|
|
free_cred(struct kcm_ntlm_cred *cred)
|
|
{
|
|
free(cred->user);
|
|
free(cred->domain);
|
|
krb5_data_free(&cred->nthash);
|
|
free(cred);
|
|
}
|
|
|
|
|
|
/*
|
|
* name
|
|
* domain
|
|
* ntlm hash
|
|
*
|
|
* Reply:
|
|
* uuid
|
|
*/
|
|
|
|
static struct kcm_ntlm_cred *
|
|
find_ntlm_cred(const char *user, const char *domain, kcm_client *client)
|
|
{
|
|
struct kcm_ntlm_cred *c;
|
|
|
|
for (c = ntlm_head; c != NULL; c = c->next)
|
|
if ((user[0] == '\0' || strcmp(user, c->user) == 0) &&
|
|
(domain == NULL || strcmp(domain, c->domain) == 0) &&
|
|
kcm_is_same_session(client, c->uid, c->session))
|
|
return c;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static krb5_error_code
|
|
kcm_op_add_ntlm_cred(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_ntlm_cred *cred, *c;
|
|
krb5_error_code ret;
|
|
|
|
cred = calloc(1, sizeof(*cred));
|
|
if (cred == NULL)
|
|
return ENOMEM;
|
|
|
|
RAND_bytes(cred->uuid, sizeof(cred->uuid));
|
|
|
|
ret = krb5_ret_stringz(request, &cred->user);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = krb5_ret_stringz(request, &cred->domain);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = krb5_ret_data(request, &cred->nthash);
|
|
if (ret)
|
|
goto error;
|
|
|
|
/* search for dups */
|
|
c = find_ntlm_cred(cred->user, cred->domain, client);
|
|
if (c) {
|
|
krb5_data hash = c->nthash;
|
|
c->nthash = cred->nthash;
|
|
cred->nthash = hash;
|
|
free_cred(cred);
|
|
cred = c;
|
|
} else {
|
|
cred->next = ntlm_head;
|
|
ntlm_head = cred;
|
|
}
|
|
|
|
cred->uid = client->uid;
|
|
cred->session = client->session;
|
|
|
|
/* write response */
|
|
(void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
|
|
|
|
return 0;
|
|
|
|
error:
|
|
free_cred(cred);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* { "HAVE_NTLM_CRED", NULL },
|
|
*
|
|
* input:
|
|
* name
|
|
* domain
|
|
*/
|
|
|
|
static krb5_error_code
|
|
kcm_op_have_ntlm_cred(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_ntlm_cred *c;
|
|
char *user = NULL, *domain = NULL;
|
|
krb5_error_code ret;
|
|
|
|
ret = krb5_ret_stringz(request, &user);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = krb5_ret_stringz(request, &domain);
|
|
if (ret)
|
|
goto error;
|
|
|
|
if (domain[0] == '\0') {
|
|
free(domain);
|
|
domain = NULL;
|
|
}
|
|
|
|
c = find_ntlm_cred(user, domain, client);
|
|
if (c == NULL)
|
|
ret = ENOENT;
|
|
|
|
error:
|
|
free(user);
|
|
if (domain)
|
|
free(domain);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* { "DEL_NTLM_CRED", NULL },
|
|
*
|
|
* input:
|
|
* name
|
|
* domain
|
|
*/
|
|
|
|
static krb5_error_code
|
|
kcm_op_del_ntlm_cred(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_ntlm_cred **cp, *c;
|
|
char *user = NULL, *domain = NULL;
|
|
krb5_error_code ret;
|
|
|
|
ret = krb5_ret_stringz(request, &user);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = krb5_ret_stringz(request, &domain);
|
|
if (ret)
|
|
goto error;
|
|
|
|
for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) {
|
|
if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 &&
|
|
kcm_is_same_session(client, (*cp)->uid, (*cp)->session))
|
|
{
|
|
c = *cp;
|
|
*cp = c->next;
|
|
|
|
free_cred(c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
error:
|
|
free(user);
|
|
free(domain);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* { "DO_NTLM_AUTH", NULL },
|
|
*
|
|
* input:
|
|
* name:string
|
|
* domain:string
|
|
* type2:data
|
|
*
|
|
* reply:
|
|
* type3:data
|
|
* flags:int32
|
|
* session-key:data
|
|
*/
|
|
|
|
#define NTLM_FLAG_SESSIONKEY 1
|
|
#define NTLM_FLAG_NTLM2_SESSION 2
|
|
#define NTLM_FLAG_KEYEX 4
|
|
|
|
static krb5_error_code
|
|
kcm_op_do_ntlm(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_ntlm_cred *c;
|
|
struct ntlm_type2 type2;
|
|
struct ntlm_type3 type3;
|
|
char *user = NULL, *domain = NULL;
|
|
struct ntlm_buf ndata, sessionkey;
|
|
krb5_data data;
|
|
krb5_error_code ret;
|
|
uint32_t flags = 0;
|
|
|
|
memset(&type2, 0, sizeof(type2));
|
|
memset(&type3, 0, sizeof(type3));
|
|
sessionkey.data = NULL;
|
|
sessionkey.length = 0;
|
|
|
|
ret = krb5_ret_stringz(request, &user);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = krb5_ret_stringz(request, &domain);
|
|
if (ret)
|
|
goto error;
|
|
|
|
if (domain[0] == '\0') {
|
|
free(domain);
|
|
domain = NULL;
|
|
}
|
|
|
|
c = find_ntlm_cred(user, domain, client);
|
|
if (c == NULL) {
|
|
ret = EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
ret = krb5_ret_data(request, &data);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ndata.data = data.data;
|
|
ndata.length = data.length;
|
|
|
|
ret = heim_ntlm_decode_type2(&ndata, &type2);
|
|
krb5_data_free(&data);
|
|
if (ret)
|
|
goto error;
|
|
|
|
if (domain && strcmp(domain, type2.targetname) == 0) {
|
|
ret = EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
type3.username = c->user;
|
|
type3.flags = type2.flags;
|
|
type3.targetname = type2.targetname;
|
|
type3.ws = rk_UNCONST("workstation");
|
|
|
|
/*
|
|
* NTLM Version 1 if no targetinfo buffer.
|
|
*/
|
|
|
|
if (1 || type2.targetinfo.length == 0) {
|
|
struct ntlm_buf sessionkey;
|
|
|
|
if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
|
|
unsigned char nonce[8];
|
|
|
|
if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
|
|
ret = EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
ret = heim_ntlm_calculate_ntlm2_sess(nonce,
|
|
type2.challenge,
|
|
c->nthash.data,
|
|
&type3.lm,
|
|
&type3.ntlm);
|
|
} else {
|
|
ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
|
|
c->nthash.length,
|
|
type2.challenge,
|
|
&type3.ntlm);
|
|
|
|
}
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
|
|
c->nthash.length,
|
|
&sessionkey,
|
|
&type3.sessionkey);
|
|
if (ret) {
|
|
if (type3.lm.data)
|
|
free(type3.lm.data);
|
|
if (type3.ntlm.data)
|
|
free(type3.ntlm.data);
|
|
goto error;
|
|
}
|
|
|
|
free(sessionkey.data);
|
|
if (ret) {
|
|
if (type3.lm.data)
|
|
free(type3.lm.data);
|
|
if (type3.ntlm.data)
|
|
free(type3.ntlm.data);
|
|
goto error;
|
|
}
|
|
flags |= NTLM_FLAG_SESSIONKEY;
|
|
#if 0
|
|
} else {
|
|
struct ntlm_buf sessionkey;
|
|
unsigned char ntlmv2[16];
|
|
struct ntlm_targetinfo ti;
|
|
|
|
/* verify infotarget */
|
|
|
|
ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
|
|
if(ret) {
|
|
_gss_ntlm_delete_sec_context(minor_status,
|
|
context_handle, NULL);
|
|
*minor_status = ret;
|
|
return GSS_S_FAILURE;
|
|
}
|
|
|
|
if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
|
|
_gss_ntlm_delete_sec_context(minor_status,
|
|
context_handle, NULL);
|
|
*minor_status = EINVAL;
|
|
return GSS_S_FAILURE;
|
|
}
|
|
|
|
ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
|
|
ctx->client->key.length,
|
|
type3.username,
|
|
name->domain,
|
|
type2.challenge,
|
|
&type2.targetinfo,
|
|
ntlmv2,
|
|
&type3.ntlm);
|
|
if (ret) {
|
|
_gss_ntlm_delete_sec_context(minor_status,
|
|
context_handle, NULL);
|
|
*minor_status = ret;
|
|
return GSS_S_FAILURE;
|
|
}
|
|
|
|
ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
|
|
&sessionkey,
|
|
&type3.sessionkey);
|
|
memset(ntlmv2, 0, sizeof(ntlmv2));
|
|
if (ret) {
|
|
_gss_ntlm_delete_sec_context(minor_status,
|
|
context_handle, NULL);
|
|
*minor_status = ret;
|
|
return GSS_S_FAILURE;
|
|
}
|
|
|
|
flags |= NTLM_FLAG_NTLM2_SESSION |
|
|
NTLM_FLAG_SESSION;
|
|
|
|
if (type3.flags & NTLM_NEG_KEYEX)
|
|
flags |= NTLM_FLAG_KEYEX;
|
|
|
|
ret = krb5_data_copy(&ctx->sessionkey,
|
|
sessionkey.data, sessionkey.length);
|
|
free(sessionkey.data);
|
|
if (ret) {
|
|
_gss_ntlm_delete_sec_context(minor_status,
|
|
context_handle, NULL);
|
|
*minor_status = ret;
|
|
return GSS_S_FAILURE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
if (flags & NTLM_FLAG_NTLM2_SESSION) {
|
|
_gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
|
|
ctx->sessionkey.data,
|
|
ctx->sessionkey.length);
|
|
_gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
|
|
ctx->sessionkey.data,
|
|
ctx->sessionkey.length);
|
|
} else {
|
|
flags |= NTLM_FLAG_SESSION;
|
|
RC4_set_key(&ctx->u.v1.crypto_recv.key,
|
|
ctx->sessionkey.length,
|
|
ctx->sessionkey.data);
|
|
RC4_set_key(&ctx->u.v1.crypto_send.key,
|
|
ctx->sessionkey.length,
|
|
ctx->sessionkey.data);
|
|
}
|
|
#endif
|
|
|
|
ret = heim_ntlm_encode_type3(&type3, &ndata);
|
|
if (ret)
|
|
goto error;
|
|
|
|
data.data = ndata.data;
|
|
data.length = ndata.length;
|
|
ret = krb5_store_data(response, data);
|
|
heim_ntlm_free_buf(&ndata);
|
|
if (ret) goto error;
|
|
|
|
ret = krb5_store_int32(response, flags);
|
|
if (ret) goto error;
|
|
|
|
data.data = sessionkey.data;
|
|
data.length = sessionkey.length;
|
|
|
|
ret = krb5_store_data(response, data);
|
|
if (ret) goto error;
|
|
|
|
error:
|
|
free(type3.username);
|
|
heim_ntlm_free_type2(&type2);
|
|
free(user);
|
|
if (domain)
|
|
free(domain);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* { "GET_NTLM_UUID_LIST", NULL }
|
|
*
|
|
* reply:
|
|
* 1 user domain
|
|
* 0 [ end of list ]
|
|
*/
|
|
|
|
static krb5_error_code
|
|
kcm_op_get_ntlm_user_list(krb5_context context,
|
|
kcm_client *client,
|
|
kcm_operation opcode,
|
|
krb5_storage *request,
|
|
krb5_storage *response)
|
|
{
|
|
struct kcm_ntlm_cred *c;
|
|
krb5_error_code ret;
|
|
|
|
for (c = ntlm_head; c != NULL; c = c->next) {
|
|
if (!kcm_is_same_session(client, c->uid, c->session))
|
|
continue;
|
|
|
|
ret = krb5_store_uint32(response, 1);
|
|
if (ret)
|
|
return ret;
|
|
ret = krb5_store_stringz(response, c->user);
|
|
if (ret)
|
|
return ret;
|
|
ret = krb5_store_stringz(response, c->domain);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
return krb5_store_uint32(response, 0);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
static struct kcm_op kcm_ops[] = {
|
|
{ "NOOP", kcm_op_noop },
|
|
{ "GET_NAME", kcm_op_get_name },
|
|
{ "RESOLVE", kcm_op_noop },
|
|
{ "GEN_NEW", kcm_op_gen_new },
|
|
{ "INITIALIZE", kcm_op_initialize },
|
|
{ "DESTROY", kcm_op_destroy },
|
|
{ "STORE", kcm_op_store },
|
|
{ "RETRIEVE", kcm_op_retrieve },
|
|
{ "GET_PRINCIPAL", kcm_op_get_principal },
|
|
{ "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list },
|
|
{ "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid },
|
|
{ "REMOVE_CRED", kcm_op_remove_cred },
|
|
{ "SET_FLAGS", kcm_op_set_flags },
|
|
{ "CHOWN", kcm_op_chown },
|
|
{ "CHMOD", kcm_op_chmod },
|
|
{ "GET_INITIAL_TICKET", kcm_op_get_initial_ticket },
|
|
{ "GET_TICKET", kcm_op_get_ticket },
|
|
{ "MOVE_CACHE", kcm_op_move_cache },
|
|
{ "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list },
|
|
{ "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid },
|
|
{ "GET_DEFAULT_CACHE", kcm_op_get_default_cache },
|
|
{ "SET_DEFAULT_CACHE", kcm_op_set_default_cache },
|
|
{ "GET_KDC_OFFSET", kcm_op_get_kdc_offset },
|
|
{ "SET_KDC_OFFSET", kcm_op_set_kdc_offset },
|
|
{ "ADD_NTLM_CRED", kcm_op_add_ntlm_cred },
|
|
{ "HAVE_USER_CRED", kcm_op_have_ntlm_cred },
|
|
{ "DEL_NTLM_CRED", kcm_op_del_ntlm_cred },
|
|
{ "DO_NTLM_AUTH", kcm_op_do_ntlm },
|
|
{ "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list }
|
|
};
|
|
|
|
|
|
const char *
|
|
kcm_op2string(kcm_operation opcode)
|
|
{
|
|
if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
|
|
return "Unknown operation";
|
|
|
|
return kcm_ops[opcode].name;
|
|
}
|
|
|
|
krb5_error_code
|
|
kcm_dispatch(krb5_context context,
|
|
kcm_client *client,
|
|
krb5_data *req_data,
|
|
krb5_data *resp_data)
|
|
{
|
|
krb5_error_code ret;
|
|
kcm_method method;
|
|
krb5_storage *req_sp = NULL;
|
|
krb5_storage *resp_sp = NULL;
|
|
uint16_t opcode;
|
|
|
|
resp_sp = krb5_storage_emem();
|
|
if (resp_sp == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
if (client->pid == -1) {
|
|
kcm_log(0, "Client had invalid process number");
|
|
ret = KRB5_FCC_INTERNAL;
|
|
goto out;
|
|
}
|
|
|
|
req_sp = krb5_storage_from_data(req_data);
|
|
if (req_sp == NULL) {
|
|
kcm_log(0, "Process %d: failed to initialize storage from data",
|
|
client->pid);
|
|
ret = KRB5_CC_IO;
|
|
goto out;
|
|
}
|
|
|
|
ret = krb5_ret_uint16(req_sp, &opcode);
|
|
if (ret) {
|
|
kcm_log(0, "Process %d: didn't send a message", client->pid);
|
|
goto out;
|
|
}
|
|
|
|
if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
|
|
kcm_log(0, "Process %d: invalid operation code %d",
|
|
client->pid, opcode);
|
|
ret = KRB5_FCC_INTERNAL;
|
|
goto out;
|
|
}
|
|
method = kcm_ops[opcode].method;
|
|
if (method == NULL) {
|
|
kcm_log(0, "Process %d: operation code %s not implemented",
|
|
client->pid, kcm_op2string(opcode));
|
|
ret = KRB5_FCC_INTERNAL;
|
|
goto out;
|
|
}
|
|
|
|
/* seek past place for status code */
|
|
krb5_storage_seek(resp_sp, 4, SEEK_SET);
|
|
|
|
ret = (*method)(context, client, opcode, req_sp, resp_sp);
|
|
|
|
out:
|
|
if (req_sp != NULL) {
|
|
krb5_storage_free(req_sp);
|
|
}
|
|
|
|
krb5_storage_seek(resp_sp, 0, SEEK_SET);
|
|
krb5_store_int32(resp_sp, ret);
|
|
|
|
ret = krb5_storage_to_data(resp_sp, resp_data);
|
|
krb5_storage_free(resp_sp);
|
|
|
|
return ret;
|
|
}
|
|
|