ae77177087
several new kerberos related libraries and applications to FreeBSD: o kgetcred(1) allows one to manually get a ticket for a particular service. o kf(1) securily forwards ticket to another host through an authenticated and encrypted stream. o kcc(1) is an umbrella program around klist(1), kswitch(1), kgetcred(1) and other user kerberos operations. klist and kswitch are just symlinks to kcc(1) now. o kswitch(1) allows you to easily switch between kerberos credentials if you're running KCM. o hxtool(1) is a certificate management tool to use with PKINIT. o string2key(1) maps a password into key. o kdigest(8) is a userland tool to access the KDC's digest interface. o kimpersonate(8) creates a "fake" ticket for a service. We also now install manpages for some lirbaries that were not installed before, libheimntlm and libhx509. - The new HEIMDAL version no longer supports Kerberos 4. All users are recommended to switch to Kerberos 5. - Weak ciphers are now disabled by default. To enable DES support (used by telnet(8)), use "allow_weak_crypto" option in krb5.conf. - libtelnet, pam_ksu and pam_krb5 are now compiled with error on warnings disabled due to the function they use (krb5_get_err_text(3)) being deprecated. I plan to work on this next. - Heimdal's KDC now require sqlite to operate. We use the bundled version and install it as libheimsqlite. If some other FreeBSD components will require it in the future we can rename it to libbsdsqlite and use for these components as well. - This is not a latest Heimdal version, the new one was released while I was working on the update. I will update it to 1.5.2 soon, as it fixes some important bugs and security issues.
964 lines
22 KiB
C
964 lines
22 KiB
C
/*
|
|
* Copyright (c) 2006 Kungliga Tekniska Högskolan
|
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
* 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 KTH 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 KTH 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 KTH 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.
|
|
*/
|
|
|
|
#include <common.h>
|
|
RCSID("$Id$");
|
|
|
|
static FILE *logfile;
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
struct client {
|
|
char *name;
|
|
struct sockaddr *sa;
|
|
socklen_t salen;
|
|
krb5_storage *sock;
|
|
int32_t capabilities;
|
|
char *target_name;
|
|
char *moniker;
|
|
krb5_storage *logsock;
|
|
int have_log;
|
|
#ifdef ENABLE_PTHREAD_SUPPORT
|
|
pthread_t thr;
|
|
#else
|
|
pid_t child;
|
|
#endif
|
|
};
|
|
|
|
static struct client **clients;
|
|
static int num_clients;
|
|
|
|
static int
|
|
init_sec_context(struct client *client,
|
|
int32_t *hContext, int32_t *hCred,
|
|
int32_t flags,
|
|
const char *targetname,
|
|
const krb5_data *itoken, krb5_data *otoken)
|
|
{
|
|
int32_t val;
|
|
krb5_data_zero(otoken);
|
|
put32(client, eInitContext);
|
|
put32(client, *hContext);
|
|
put32(client, *hCred);
|
|
put32(client, flags);
|
|
putstring(client, targetname);
|
|
putdata(client, *itoken);
|
|
ret32(client, *hContext);
|
|
ret32(client, val);
|
|
retdata(client, *otoken);
|
|
return val;
|
|
}
|
|
|
|
static int
|
|
accept_sec_context(struct client *client,
|
|
int32_t *hContext,
|
|
int32_t flags,
|
|
const krb5_data *itoken,
|
|
krb5_data *otoken,
|
|
int32_t *hDelegCred)
|
|
{
|
|
int32_t val;
|
|
krb5_data_zero(otoken);
|
|
put32(client, eAcceptContext);
|
|
put32(client, *hContext);
|
|
put32(client, flags);
|
|
putdata(client, *itoken);
|
|
ret32(client, *hContext);
|
|
ret32(client, val);
|
|
retdata(client, *otoken);
|
|
ret32(client, *hDelegCred);
|
|
return val;
|
|
}
|
|
|
|
static int
|
|
acquire_cred(struct client *client,
|
|
const char *username,
|
|
const char *password,
|
|
int32_t flags,
|
|
int32_t *hCred)
|
|
{
|
|
int32_t val;
|
|
put32(client, eAcquireCreds);
|
|
putstring(client, username);
|
|
putstring(client, password);
|
|
put32(client, flags);
|
|
ret32(client, val);
|
|
ret32(client, *hCred);
|
|
return val;
|
|
}
|
|
|
|
static int
|
|
toast_resource(struct client *client,
|
|
int32_t hCred)
|
|
{
|
|
int32_t val;
|
|
put32(client, eToastResource);
|
|
put32(client, hCred);
|
|
ret32(client, val);
|
|
return val;
|
|
}
|
|
|
|
static int
|
|
goodbye(struct client *client)
|
|
{
|
|
put32(client, eGoodBye);
|
|
return GSMERR_OK;
|
|
}
|
|
|
|
static int
|
|
get_targetname(struct client *client,
|
|
char **target)
|
|
{
|
|
put32(client, eGetTargetName);
|
|
retstring(client, *target);
|
|
return GSMERR_OK;
|
|
}
|
|
|
|
static int32_t
|
|
encrypt_token(struct client *client, int32_t hContext, int32_t flags,
|
|
krb5_data *in, krb5_data *out)
|
|
{
|
|
int32_t val;
|
|
put32(client, eEncrypt);
|
|
put32(client, hContext);
|
|
put32(client, flags);
|
|
put32(client, 0);
|
|
putdata(client, *in);
|
|
ret32(client, val);
|
|
retdata(client, *out);
|
|
return val;
|
|
}
|
|
|
|
static int32_t
|
|
decrypt_token(struct client *client, int32_t hContext, int flags,
|
|
krb5_data *in, krb5_data *out)
|
|
{
|
|
int32_t val;
|
|
put32(client, eDecrypt);
|
|
put32(client, hContext);
|
|
put32(client, flags);
|
|
put32(client, 0);
|
|
putdata(client, *in);
|
|
ret32(client, val);
|
|
retdata(client, *out);
|
|
return val;
|
|
}
|
|
|
|
static int32_t
|
|
wrap_token_ext(struct client *client, int32_t hContext, int32_t flags,
|
|
int32_t bflags, krb5_data *header, krb5_data *in, krb5_data *trailer,
|
|
krb5_data *out)
|
|
{
|
|
int32_t val;
|
|
put32(client, eWrapExt);
|
|
put32(client, hContext);
|
|
put32(client, flags);
|
|
put32(client, bflags);
|
|
putdata(client, *header);
|
|
putdata(client, *in);
|
|
putdata(client, *trailer);
|
|
ret32(client, val);
|
|
retdata(client, *out);
|
|
return val;
|
|
}
|
|
|
|
static int32_t
|
|
unwrap_token_ext(struct client *client, int32_t hContext, int32_t flags,
|
|
int32_t bflags, krb5_data *header, krb5_data *in, krb5_data *trailer,
|
|
krb5_data *out)
|
|
{
|
|
int32_t val;
|
|
put32(client, eUnwrapExt);
|
|
put32(client, hContext);
|
|
put32(client, flags);
|
|
put32(client, bflags);
|
|
putdata(client, *header);
|
|
putdata(client, *in);
|
|
putdata(client, *trailer);
|
|
ret32(client, val);
|
|
retdata(client, *out);
|
|
return val;
|
|
}
|
|
|
|
static int32_t
|
|
get_mic(struct client *client, int32_t hContext,
|
|
krb5_data *in, krb5_data *mic)
|
|
{
|
|
int32_t val;
|
|
put32(client, eSign);
|
|
put32(client, hContext);
|
|
put32(client, 0);
|
|
put32(client, 0);
|
|
putdata(client, *in);
|
|
ret32(client, val);
|
|
retdata(client, *mic);
|
|
return val;
|
|
}
|
|
|
|
static int32_t
|
|
verify_mic(struct client *client, int32_t hContext,
|
|
krb5_data *in, krb5_data *mic)
|
|
{
|
|
int32_t val;
|
|
put32(client, eVerify);
|
|
put32(client, hContext);
|
|
put32(client, 0);
|
|
put32(client, 0);
|
|
putdata(client, *in);
|
|
putdata(client, *mic);
|
|
ret32(client, val);
|
|
return val;
|
|
}
|
|
|
|
|
|
static int32_t
|
|
get_version_capa(struct client *client,
|
|
int32_t *version, int32_t *capa,
|
|
char **version_str)
|
|
{
|
|
put32(client, eGetVersionAndCapabilities);
|
|
ret32(client, *version);
|
|
ret32(client, *capa);
|
|
retstring(client, *version_str);
|
|
return GSMERR_OK;
|
|
}
|
|
|
|
static int32_t
|
|
get_moniker(struct client *client,
|
|
char **moniker)
|
|
{
|
|
put32(client, eGetMoniker);
|
|
retstring(client, *moniker);
|
|
return GSMERR_OK;
|
|
}
|
|
|
|
static int
|
|
wait_log(struct client *c)
|
|
{
|
|
int32_t port;
|
|
struct sockaddr_storage sast;
|
|
socklen_t salen = sizeof(sast);
|
|
int fd, fd2, ret;
|
|
|
|
memset(&sast, 0, sizeof(sast));
|
|
|
|
assert(sizeof(sast) >= c->salen);
|
|
|
|
fd = socket(c->sa->sa_family, SOCK_STREAM, 0);
|
|
if (fd < 0)
|
|
err(1, "failed to build socket for %s's logging port", c->moniker);
|
|
|
|
((struct sockaddr *)&sast)->sa_family = c->sa->sa_family;
|
|
ret = bind(fd, (struct sockaddr *)&sast, c->salen);
|
|
if (ret < 0)
|
|
err(1, "failed to bind %s's logging port", c->moniker);
|
|
|
|
if (listen(fd, SOMAXCONN) < 0)
|
|
err(1, "failed to listen %s's logging port", c->moniker);
|
|
|
|
salen = sizeof(sast);
|
|
ret = getsockname(fd, (struct sockaddr *)&sast, &salen);
|
|
if (ret < 0)
|
|
err(1, "failed to get address of local socket for %s", c->moniker);
|
|
|
|
port = socket_get_port((struct sockaddr *)&sast);
|
|
|
|
put32(c, eSetLoggingSocket);
|
|
put32(c, ntohs(port));
|
|
|
|
salen = sizeof(sast);
|
|
fd2 = accept(fd, (struct sockaddr *)&sast, &salen);
|
|
if (fd2 < 0)
|
|
err(1, "failed to accept local socket for %s", c->moniker);
|
|
close(fd);
|
|
|
|
return fd2;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
build_context(struct client *ipeer, struct client *apeer,
|
|
int32_t flags, int32_t hCred,
|
|
int32_t *iContext, int32_t *aContext, int32_t *hDelegCred)
|
|
{
|
|
int32_t val = GSMERR_ERROR, ic = 0, ac = 0, deleg = 0;
|
|
krb5_data itoken, otoken;
|
|
int iDone = 0, aDone = 0;
|
|
int step = 0;
|
|
int first_call = 0x80;
|
|
|
|
if (apeer->target_name == NULL)
|
|
errx(1, "apeer %s have no target name", apeer->name);
|
|
|
|
krb5_data_zero(&itoken);
|
|
|
|
while (!iDone || !aDone) {
|
|
|
|
if (iDone) {
|
|
warnx("iPeer already done, aPeer want extra rtt");
|
|
val = GSMERR_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
val = init_sec_context(ipeer, &ic, &hCred, flags|first_call,
|
|
apeer->target_name, &itoken, &otoken);
|
|
step++;
|
|
switch(val) {
|
|
case GSMERR_OK:
|
|
iDone = 1;
|
|
if (aDone)
|
|
continue;
|
|
break;
|
|
case GSMERR_CONTINUE_NEEDED:
|
|
break;
|
|
default:
|
|
warnx("iPeer %s failed with %d (step %d)",
|
|
ipeer->name, (int)val, step);
|
|
goto out;
|
|
}
|
|
|
|
if (aDone) {
|
|
warnx("aPeer already done, iPeer want extra rtt");
|
|
val = GSMERR_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
val = accept_sec_context(apeer, &ac, flags|first_call,
|
|
&otoken, &itoken, &deleg);
|
|
step++;
|
|
switch(val) {
|
|
case GSMERR_OK:
|
|
aDone = 1;
|
|
if (iDone)
|
|
continue;
|
|
break;
|
|
case GSMERR_CONTINUE_NEEDED:
|
|
break;
|
|
default:
|
|
warnx("aPeer %s failed with %d (step %d)",
|
|
apeer->name, (int)val, step);
|
|
val = GSMERR_ERROR;
|
|
goto out;
|
|
}
|
|
first_call = 0;
|
|
val = GSMERR_OK;
|
|
}
|
|
|
|
if (iContext == NULL || val != GSMERR_OK) {
|
|
if (ic)
|
|
toast_resource(ipeer, ic);
|
|
if (iContext)
|
|
*iContext = 0;
|
|
} else
|
|
*iContext = ic;
|
|
|
|
if (aContext == NULL || val != GSMERR_OK) {
|
|
if (ac)
|
|
toast_resource(apeer, ac);
|
|
if (aContext)
|
|
*aContext = 0;
|
|
} else
|
|
*aContext = ac;
|
|
|
|
if (hDelegCred == NULL || val != GSMERR_OK) {
|
|
if (deleg)
|
|
toast_resource(apeer, deleg);
|
|
if (hDelegCred)
|
|
*hDelegCred = 0;
|
|
} else
|
|
*hDelegCred = deleg;
|
|
|
|
out:
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
test_mic(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
|
|
{
|
|
krb5_data msg, mic;
|
|
int32_t val;
|
|
|
|
msg.data = "foo";
|
|
msg.length = 3;
|
|
|
|
krb5_data_zero(&mic);
|
|
|
|
val = get_mic(c1, hc1, &msg, &mic);
|
|
if (val)
|
|
errx(1, "get_mic failed to host: %s", c1->moniker);
|
|
val = verify_mic(c2, hc2, &msg, &mic);
|
|
if (val)
|
|
errx(1, "verify_mic failed to host: %s", c2->moniker);
|
|
|
|
krb5_data_free(&mic);
|
|
}
|
|
|
|
static int32_t
|
|
test_wrap(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2,
|
|
int conf)
|
|
{
|
|
krb5_data msg, wrapped, out;
|
|
int32_t val;
|
|
|
|
msg.data = "foo";
|
|
msg.length = 3;
|
|
|
|
krb5_data_zero(&wrapped);
|
|
krb5_data_zero(&out);
|
|
|
|
val = encrypt_token(c1, hc1, conf, &msg, &wrapped);
|
|
if (val) {
|
|
warnx("encrypt_token failed to host: %s", c1->moniker);
|
|
return val;
|
|
}
|
|
val = decrypt_token(c2, hc2, conf, &wrapped, &out);
|
|
if (val) {
|
|
krb5_data_free(&wrapped);
|
|
warnx("decrypt_token failed to host: %s", c2->moniker);
|
|
return val;
|
|
}
|
|
|
|
if (msg.length != out.length) {
|
|
warnx("decrypted'ed token have wrong length (%lu != %lu)",
|
|
(unsigned long)msg.length, (unsigned long)out.length);
|
|
val = GSMERR_ERROR;
|
|
} else if (memcmp(msg.data, out.data, msg.length) != 0) {
|
|
warnx("decryptd'ed token have wrong data");
|
|
val = GSMERR_ERROR;
|
|
}
|
|
|
|
krb5_data_free(&wrapped);
|
|
krb5_data_free(&out);
|
|
return val;
|
|
}
|
|
|
|
static int32_t
|
|
test_wrap_ext(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2,
|
|
int conf, int bflags)
|
|
{
|
|
krb5_data header, msg, trailer, wrapped, out;
|
|
int32_t val;
|
|
|
|
header.data = "header";
|
|
header.length = 6;
|
|
|
|
msg.data = "0123456789abcdef"; /* padded for most enctypes */
|
|
msg.length = 32;
|
|
|
|
trailer.data = "trailer";
|
|
trailer.length = 7;
|
|
|
|
krb5_data_zero(&wrapped);
|
|
krb5_data_zero(&out);
|
|
|
|
val = wrap_token_ext(c1, hc1, conf, bflags, &header, &msg, &trailer, &wrapped);
|
|
if (val) {
|
|
warnx("encrypt_token failed to host: %s", c1->moniker);
|
|
return val;
|
|
}
|
|
val = unwrap_token_ext(c2, hc2, conf, bflags, &header, &wrapped, &trailer, &out);
|
|
if (val) {
|
|
krb5_data_free(&wrapped);
|
|
warnx("decrypt_token failed to host: %s", c2->moniker);
|
|
return val;
|
|
}
|
|
|
|
if (msg.length != out.length) {
|
|
warnx("decrypted'ed token have wrong length (%lu != %lu)",
|
|
(unsigned long)msg.length, (unsigned long)out.length);
|
|
val = GSMERR_ERROR;
|
|
} else if (memcmp(msg.data, out.data, msg.length) != 0) {
|
|
warnx("decryptd'ed token have wrong data");
|
|
val = GSMERR_ERROR;
|
|
}
|
|
|
|
krb5_data_free(&wrapped);
|
|
krb5_data_free(&out);
|
|
return val;
|
|
}
|
|
|
|
|
|
static int32_t
|
|
test_token(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2, int wrap_ext)
|
|
{
|
|
int32_t val;
|
|
int i;
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
/* mic */
|
|
test_mic(c1, hc1, c2, hc2);
|
|
test_mic(c2, hc2, c1, hc1);
|
|
|
|
/* wrap */
|
|
val = test_wrap(c1, hc1, c2, hc2, 0);
|
|
if (val) return val;
|
|
val = test_wrap(c2, hc2, c1, hc1, 0);
|
|
if (val) return val;
|
|
|
|
val = test_wrap(c1, hc1, c2, hc2, 1);
|
|
if (val) return val;
|
|
val = test_wrap(c2, hc2, c1, hc1, 1);
|
|
if (val) return val;
|
|
|
|
if (wrap_ext) {
|
|
/* wrap ext */
|
|
val = test_wrap_ext(c1, hc1, c2, hc2, 1, 0);
|
|
if (val) return val;
|
|
val = test_wrap_ext(c2, hc2, c1, hc1, 1, 0);
|
|
if (val) return val;
|
|
|
|
val = test_wrap_ext(c1, hc1, c2, hc2, 1, 1);
|
|
if (val) return val;
|
|
val = test_wrap_ext(c2, hc2, c1, hc1, 1, 1);
|
|
if (val) return val;
|
|
|
|
val = test_wrap_ext(c1, hc1, c2, hc2, 0, 0);
|
|
if (val) return val;
|
|
val = test_wrap_ext(c2, hc2, c1, hc1, 0, 0);
|
|
if (val) return val;
|
|
|
|
val = test_wrap_ext(c1, hc1, c2, hc2, 0, 1);
|
|
if (val) return val;
|
|
val = test_wrap_ext(c2, hc2, c1, hc1, 0, 1);
|
|
if (val) return val;
|
|
}
|
|
}
|
|
return GSMERR_OK;
|
|
}
|
|
|
|
static int
|
|
log_function(void *ptr)
|
|
{
|
|
struct client *c = ptr;
|
|
int32_t cmd, line;
|
|
char *file, *string;
|
|
|
|
while (1) {
|
|
if (krb5_ret_int32(c->logsock, &cmd))
|
|
goto out;
|
|
|
|
switch (cmd) {
|
|
case eLogSetMoniker:
|
|
if (krb5_ret_string(c->logsock, &file))
|
|
goto out;
|
|
free(file);
|
|
break;
|
|
case eLogInfo:
|
|
case eLogFailure:
|
|
if (krb5_ret_string(c->logsock, &file))
|
|
goto out;
|
|
if (krb5_ret_int32(c->logsock, &line))
|
|
goto out;
|
|
if (krb5_ret_string(c->logsock, &string))
|
|
goto out;
|
|
printf("%s:%lu: %s\n",
|
|
file, (unsigned long)line, string);
|
|
fprintf(logfile, "%s:%lu: %s\n",
|
|
file, (unsigned long)line, string);
|
|
fflush(logfile);
|
|
free(file);
|
|
free(string);
|
|
if (krb5_store_int32(c->logsock, 0))
|
|
goto out;
|
|
break;
|
|
default:
|
|
errx(1, "client send bad log command: %d", (int)cmd);
|
|
}
|
|
}
|
|
out:
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
connect_client(const char *slave)
|
|
{
|
|
char *name, *port;
|
|
struct client *c = ecalloc(1, sizeof(*c));
|
|
struct addrinfo hints, *res0, *res;
|
|
int ret, fd;
|
|
|
|
name = estrdup(slave);
|
|
port = strchr(name, ':');
|
|
if (port == NULL)
|
|
errx(1, "port missing from %s", name);
|
|
*port++ = 0;
|
|
|
|
c->name = estrdup(slave);
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = PF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
ret = getaddrinfo(name, port, &hints, &res0);
|
|
if (ret)
|
|
errx(1, "error resolving %s", name);
|
|
|
|
for (res = res0, fd = -1; res; res = res->ai_next) {
|
|
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (fd < 0)
|
|
continue;
|
|
if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
|
|
close(fd);
|
|
fd = -1;
|
|
continue;
|
|
}
|
|
c->sa = ecalloc(1, res->ai_addrlen);
|
|
memcpy(c->sa, res->ai_addr, res->ai_addrlen);
|
|
c->salen = res->ai_addrlen;
|
|
break; /* okay we got one */
|
|
}
|
|
if (fd < 0)
|
|
err(1, "connect to host: %s", name);
|
|
freeaddrinfo(res);
|
|
|
|
c->sock = krb5_storage_from_fd(fd);
|
|
close(fd);
|
|
if (c->sock == NULL)
|
|
errx(1, "krb5_storage_from_fd");
|
|
|
|
{
|
|
int32_t version;
|
|
char *str = NULL;
|
|
get_version_capa(c, &version, &c->capabilities, &str);
|
|
if (str) {
|
|
free(str);
|
|
}
|
|
if (c->capabilities & HAS_MONIKER)
|
|
get_moniker(c, &c->moniker);
|
|
else
|
|
c->moniker = c->name;
|
|
if (c->capabilities & ISSERVER)
|
|
get_targetname(c, &c->target_name);
|
|
}
|
|
|
|
if (logfile) {
|
|
int fd;
|
|
|
|
printf("starting log socket to client %s\n", c->moniker);
|
|
|
|
fd = wait_log(c);
|
|
|
|
c->logsock = krb5_storage_from_fd(fd);
|
|
close(fd);
|
|
if (c->logsock == NULL)
|
|
errx(1, "failed to create log krb5_storage");
|
|
#ifdef ENABLE_PTHREAD_SUPPORT
|
|
pthread_create(&c->thr, NULL, log_function, c);
|
|
#else
|
|
c->child = fork();
|
|
if (c->child == -1)
|
|
errx(1, "failed to fork");
|
|
else if (c->child == 0) {
|
|
log_function(c);
|
|
fclose(logfile);
|
|
exit(0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
clients = erealloc(clients, (num_clients + 1) * sizeof(*clients));
|
|
|
|
clients[num_clients] = c;
|
|
num_clients++;
|
|
|
|
free(name);
|
|
}
|
|
|
|
static struct client *
|
|
get_client(const char *slave)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < num_clients; i++)
|
|
if (strcmp(slave, clients[i]->name) == 0)
|
|
return clients[i];
|
|
errx(1, "failed to find client %s", slave);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
static int version_flag;
|
|
static int help_flag;
|
|
static int wrap_ext = 0;
|
|
static char *logfile_str;
|
|
static getarg_strings principals;
|
|
static getarg_strings slaves;
|
|
|
|
struct getargs args[] = {
|
|
{ "principals", 0, arg_strings, &principals, "Test principal",
|
|
NULL },
|
|
{ "slaves", 0, arg_strings, &slaves, "Slaves",
|
|
NULL },
|
|
{ "log-file", 0, arg_string, &logfile_str, "Logfile",
|
|
NULL },
|
|
{ "wrap-ext", 0, arg_flag, &wrap_ext, "test wrap extended",
|
|
NULL },
|
|
{ "version", 0, arg_flag, &version_flag, "Print version",
|
|
NULL },
|
|
{ "help", 0, arg_flag, &help_flag, NULL,
|
|
NULL }
|
|
};
|
|
|
|
static void
|
|
usage(int ret)
|
|
{
|
|
arg_printusage (args,
|
|
sizeof(args) / sizeof(args[0]),
|
|
NULL,
|
|
"");
|
|
exit (ret);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int optidx= 0;
|
|
char *user;
|
|
char *password;
|
|
char ***list, **p;
|
|
size_t num_list, i, j, k;
|
|
int failed = 0;
|
|
|
|
setprogname (argv[0]);
|
|
|
|
if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
|
|
usage (1);
|
|
|
|
if (help_flag)
|
|
usage (0);
|
|
|
|
if (version_flag) {
|
|
print_version (NULL);
|
|
return 0;
|
|
}
|
|
|
|
if (optidx != argc)
|
|
usage (1);
|
|
|
|
if (principals.num_strings == 0)
|
|
errx(1, "no principals");
|
|
|
|
user = estrdup(principals.strings[0]);
|
|
password = strchr(user, ':');
|
|
if (password == NULL)
|
|
errx(1, "password missing from %s", user);
|
|
*password++ = 0;
|
|
|
|
if (slaves.num_strings == 0)
|
|
errx(1, "no principals");
|
|
|
|
if (logfile_str) {
|
|
printf("open logfile %s\n", logfile_str);
|
|
logfile = fopen(logfile_str, "w+");
|
|
if (logfile == NULL)
|
|
err(1, "failed to open: %s", logfile_str);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
list = permutate_all(&slaves, &num_list);
|
|
|
|
/*
|
|
* Set up connection to all clients
|
|
*/
|
|
|
|
printf("Connecting to slaves\n");
|
|
for (i = 0; i < slaves.num_strings; i++)
|
|
connect_client(slaves.strings[i]);
|
|
|
|
/*
|
|
* Test acquire credentials
|
|
*/
|
|
|
|
printf("Test acquire credentials\n");
|
|
for (i = 0; i < slaves.num_strings; i++) {
|
|
int32_t hCred, val;
|
|
|
|
val = acquire_cred(clients[i], user, password, 1, &hCred);
|
|
if (val != GSMERR_OK) {
|
|
warnx("Failed to acquire_cred on host %s: %d",
|
|
clients[i]->moniker, (int)val);
|
|
failed = 1;
|
|
} else
|
|
toast_resource(clients[i], hCred);
|
|
}
|
|
|
|
if (failed)
|
|
goto out;
|
|
|
|
/*
|
|
* First test if all slaves can build context to them-self.
|
|
*/
|
|
|
|
printf("Self context tests\n");
|
|
for (i = 0; i < num_clients; i++) {
|
|
int32_t hCred, val, delegCred;
|
|
int32_t clientC, serverC;
|
|
struct client *c = clients[i];
|
|
|
|
if (c->target_name == NULL)
|
|
continue;
|
|
|
|
printf("%s connects to self using %s\n",
|
|
c->moniker, c->target_name);
|
|
|
|
val = acquire_cred(c, user, password, 1, &hCred);
|
|
if (val != GSMERR_OK)
|
|
errx(1, "failed to acquire_cred: %d", (int)val);
|
|
|
|
val = build_context(c, c,
|
|
GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
|
|
GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
|
|
GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
|
|
hCred, &clientC, &serverC, &delegCred);
|
|
if (val == GSMERR_OK) {
|
|
test_token(c, clientC, c, serverC, wrap_ext);
|
|
toast_resource(c, clientC);
|
|
toast_resource(c, serverC);
|
|
if (delegCred)
|
|
toast_resource(c, delegCred);
|
|
} else {
|
|
warnx("build_context failed: %d", (int)val);
|
|
}
|
|
/*
|
|
*
|
|
*/
|
|
|
|
val = build_context(c, c,
|
|
GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG,
|
|
hCred, &clientC, &serverC, &delegCred);
|
|
if (val == GSMERR_OK) {
|
|
test_token(c, clientC, c, serverC, wrap_ext);
|
|
toast_resource(c, clientC);
|
|
toast_resource(c, serverC);
|
|
if (delegCred)
|
|
toast_resource(c, delegCred);
|
|
} else {
|
|
warnx("build_context failed: %d", (int)val);
|
|
}
|
|
|
|
toast_resource(c, hCred);
|
|
}
|
|
/*
|
|
* Build contexts though all entries in each lists, including the
|
|
* step from the last entry to the first, ie treat the list as a
|
|
* circle.
|
|
*
|
|
* Only follow the delegated credential, but test "all"
|
|
* flags. (XXX only do deleg|mutual right now.
|
|
*/
|
|
|
|
printf("\"All\" permutation tests\n");
|
|
|
|
for (i = 0; i < num_list; i++) {
|
|
int32_t hCred, val, delegCred = 0;
|
|
int32_t clientC = 0, serverC = 0;
|
|
struct client *client, *server;
|
|
|
|
p = list[i];
|
|
|
|
client = get_client(p[0]);
|
|
|
|
val = acquire_cred(client, user, password, 1, &hCred);
|
|
if (val != GSMERR_OK)
|
|
errx(1, "failed to acquire_cred: %d", (int)val);
|
|
|
|
for (j = 1; j < num_clients + 1; j++) {
|
|
server = get_client(p[j % num_clients]);
|
|
|
|
if (server->target_name == NULL)
|
|
break;
|
|
|
|
for (k = 1; k < j; k++)
|
|
printf("\t");
|
|
printf("%s -> %s\n", client->moniker, server->moniker);
|
|
|
|
val = build_context(client, server,
|
|
GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
|
|
GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
|
|
GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
|
|
hCred, &clientC, &serverC, &delegCred);
|
|
if (val != GSMERR_OK) {
|
|
warnx("build_context failed: %d", (int)val);
|
|
break;
|
|
}
|
|
|
|
val = test_token(client, clientC, server, serverC, wrap_ext);
|
|
if (val)
|
|
break;
|
|
|
|
toast_resource(client, clientC);
|
|
toast_resource(server, serverC);
|
|
if (!delegCred) {
|
|
warnx("no delegated cred on %s", server->moniker);
|
|
break;
|
|
}
|
|
toast_resource(client, hCred);
|
|
hCred = delegCred;
|
|
client = server;
|
|
}
|
|
if (hCred)
|
|
toast_resource(client, hCred);
|
|
}
|
|
|
|
/*
|
|
* Close all connections to clients
|
|
*/
|
|
|
|
out:
|
|
printf("sending goodbye and waiting for log sockets\n");
|
|
for (i = 0; i < num_clients; i++) {
|
|
goodbye(clients[i]);
|
|
if (clients[i]->logsock) {
|
|
#ifdef ENABLE_PTHREAD_SUPPORT
|
|
pthread_join(&clients[i]->thr, NULL);
|
|
#else
|
|
waitpid(clients[i]->child, NULL, 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
printf("done\n");
|
|
|
|
return 0;
|
|
}
|