328 lines
8.6 KiB
C
328 lines
8.6 KiB
C
/* This file is part of the Project Athena Zephyr Notification System.
|
|
* It contains source for the ZMakeAuthentication function.
|
|
*
|
|
* Created by: Robert French
|
|
*
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 1987 by the Massachusetts Institute of Technology.
|
|
* For copying and distribution information, see the file
|
|
* "mit-copyright.h".
|
|
*/
|
|
|
|
#include <internal.h>
|
|
|
|
#ifndef lint
|
|
static const char rcsid_ZMakeAuthentication_c[] = "$Id$";
|
|
#endif
|
|
|
|
#ifdef HAVE_KRB4
|
|
#include <krb_err.h>
|
|
#endif
|
|
|
|
#if defined(HAVE_KRB5) && !HAVE_KRB5_FREE_DATA
|
|
#define krb5_free_data(ctx, dat) free((dat)->data)
|
|
#endif
|
|
|
|
Code_t
|
|
ZResetAuthentication(void)
|
|
{
|
|
return ZERR_NONE;
|
|
}
|
|
|
|
Code_t
|
|
ZMakeAuthentication(register ZNotice_t *notice,
|
|
char *buffer,
|
|
int buffer_len,
|
|
int *len)
|
|
{
|
|
#ifdef HAVE_KRB5
|
|
return ZMakeZcodeAuthentication(notice, buffer, buffer_len, len/*?XXX*/);
|
|
#else
|
|
#ifdef HAVE_KRB4
|
|
int result;
|
|
KTEXT_ST authent;
|
|
char *cstart, *cend;
|
|
ZChecksum_t checksum;
|
|
CREDENTIALS cred;
|
|
C_Block *session;
|
|
|
|
result = krb_mk_req(&authent, SERVER_SERVICE,
|
|
SERVER_INSTANCE, __Zephyr_realm, 0);
|
|
if (result != MK_AP_OK)
|
|
return (result+krb_err_base);
|
|
result = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE,
|
|
__Zephyr_realm, &cred);
|
|
if (result != KSUCCESS)
|
|
return (result+krb_err_base);
|
|
|
|
session = (C_Block *)cred.session;
|
|
|
|
notice->z_auth = 1;
|
|
notice->z_authent_len = authent.length;
|
|
notice->z_ascii_authent = (char *)malloc((unsigned)authent.length*3);
|
|
/* zero length authent is an error, so malloc(0) is not a problem */
|
|
if (!notice->z_ascii_authent)
|
|
return (ENOMEM);
|
|
if ((result = ZMakeAscii(notice->z_ascii_authent,
|
|
authent.length*3,
|
|
authent.dat,
|
|
authent.length)) != ZERR_NONE) {
|
|
free(notice->z_ascii_authent);
|
|
return (result);
|
|
}
|
|
result = Z_FormatRawHeader(notice, buffer, buffer_len, len, &cstart,
|
|
&cend);
|
|
free(notice->z_ascii_authent);
|
|
notice->z_authent_len = 0;
|
|
if (result)
|
|
return(result);
|
|
|
|
/* Compute a checksum over the header and message. */
|
|
checksum = des_quad_cksum((unsigned char *)buffer, NULL, cstart - buffer, 0, session);
|
|
checksum ^= des_quad_cksum((unsigned char *)cend, NULL, buffer + *len - cend, 0,
|
|
session);
|
|
checksum ^= des_quad_cksum((unsigned char *)notice->z_message, NULL, notice->z_message_len,
|
|
0, session);
|
|
notice->z_checksum = checksum;
|
|
ZMakeAscii32(cstart, buffer + buffer_len - cstart, checksum);
|
|
|
|
return (ZERR_NONE);
|
|
#else
|
|
notice->z_checksum = 0;
|
|
notice->z_auth = 1;
|
|
notice->z_authent_len = 0;
|
|
notice->z_ascii_authent = "";
|
|
return (Z_FormatRawHeader(notice, buffer, buffer_len, len, NULL, NULL));
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
Code_t
|
|
Z_MakeAuthenticationSaveKey(register ZNotice_t *notice,
|
|
char *buffer,
|
|
int buffer_len,
|
|
int *len)
|
|
{
|
|
#ifndef HAVE_KRB5
|
|
/* Key management not implemented for krb4. */
|
|
return ZMakeAuthentication(notice, buffer, buffer_len, len);
|
|
#else
|
|
Code_t result;
|
|
krb5_creds *creds = NULL;
|
|
krb5_keyblock *keyblock;
|
|
struct _Z_SessionKey *savedkey;
|
|
|
|
/* Look up creds and checksum the notice. */
|
|
if ((result = ZGetCreds(&creds)))
|
|
return result;
|
|
if ((result = Z_MakeZcodeAuthentication(notice, buffer, buffer_len, len,
|
|
creds))) {
|
|
krb5_free_creds(Z_krb5_ctx, creds);
|
|
return result;
|
|
}
|
|
|
|
/* Save the key. */
|
|
keyblock = Z_credskey(creds);
|
|
|
|
if (Z_keys_head &&
|
|
Z_keys_head->keyblock->enctype == keyblock->enctype &&
|
|
Z_keys_head->keyblock->length == keyblock->length &&
|
|
memcmp(Z_keys_head->keyblock->contents, keyblock->contents,
|
|
keyblock->length) == 0) {
|
|
/*
|
|
* Optimization: if the key hasn't changed, replace the current entry,
|
|
* rather than make a new one.
|
|
*/
|
|
Z_keys_head->send_time = time(NULL);
|
|
Z_keys_head->first_use = 0;
|
|
} else {
|
|
savedkey = (struct _Z_SessionKey *)malloc(sizeof(struct _Z_SessionKey));
|
|
if (!savedkey) {
|
|
krb5_free_creds(Z_krb5_ctx, creds);
|
|
return ENOMEM;
|
|
}
|
|
|
|
if ((result = krb5_copy_keyblock(Z_krb5_ctx, keyblock, &savedkey->keyblock))) {
|
|
free(savedkey);
|
|
krb5_free_creds(Z_krb5_ctx, creds);
|
|
return result;
|
|
}
|
|
savedkey->send_time = time(NULL);
|
|
savedkey->first_use = 0;
|
|
|
|
savedkey->prev = NULL;
|
|
savedkey->next = Z_keys_head;
|
|
if (Z_keys_head)
|
|
Z_keys_head->prev = savedkey;
|
|
Z_keys_head = savedkey;
|
|
if (!Z_keys_tail)
|
|
Z_keys_tail = savedkey;
|
|
}
|
|
|
|
krb5_free_creds(Z_krb5_ctx, creds);
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
/* only used by server? */
|
|
Code_t
|
|
ZMakeZcodeAuthentication(register ZNotice_t *notice,
|
|
char *buffer,
|
|
int buffer_len,
|
|
int *phdr_len)
|
|
{
|
|
return ZMakeZcodeRealmAuthentication(notice, buffer, buffer_len, phdr_len,
|
|
__Zephyr_realm);
|
|
}
|
|
|
|
Code_t
|
|
ZMakeZcodeRealmAuthentication(register ZNotice_t *notice,
|
|
char *buffer,
|
|
int buffer_len,
|
|
int *phdr_len,
|
|
char *realm)
|
|
{
|
|
#ifdef HAVE_KRB5
|
|
Code_t result;
|
|
krb5_creds *creds = NULL;
|
|
|
|
result = ZGetCredsRealm(&creds, realm);
|
|
if (!result)
|
|
result = Z_MakeZcodeAuthentication(notice, buffer, buffer_len, phdr_len,
|
|
creds);
|
|
if (creds != NULL)
|
|
krb5_free_creds(Z_krb5_ctx, creds);
|
|
return result;
|
|
#else /* HAVE_KRB5 */
|
|
return ZERR_INTERNAL;
|
|
#endif
|
|
}
|
|
|
|
#ifdef HAVE_KRB5
|
|
Code_t
|
|
Z_MakeZcodeAuthentication(register ZNotice_t *notice,
|
|
char *buffer,
|
|
int buffer_len,
|
|
int *phdr_len,
|
|
krb5_creds *creds)
|
|
{
|
|
krb5_error_code result = 0;
|
|
krb5_keyblock *keyblock;
|
|
krb5_auth_context authctx;
|
|
krb5_data *authent;
|
|
char *cksum_start, *cstart, *cend;
|
|
int cksum_len, zcode_len = 0, phdr_adj = 0;
|
|
|
|
notice->z_ascii_authent = NULL;
|
|
|
|
keyblock = Z_credskey(creds);
|
|
|
|
authent = (krb5_data *)malloc(sizeof(krb5_data));
|
|
if (authent == NULL)
|
|
result = ENOMEM;
|
|
authent->data = NULL; /* so that we can blithely krb5_fre_data_contents on
|
|
the way out */
|
|
|
|
if (!result)
|
|
result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
|
|
|
|
if (!result) {
|
|
result = krb5_mk_req_extended(Z_krb5_ctx, &authctx, 0 /* options */,
|
|
0 /* in_data */, creds, authent);
|
|
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
|
}
|
|
if (!result || result == KRB5KRB_AP_ERR_TKT_EXPIRED) {
|
|
notice->z_auth = 1;
|
|
if (result == 0) {
|
|
notice->z_authent_len = authent->length;
|
|
} else {
|
|
notice->z_authent_len = 0;
|
|
result = 0;
|
|
}
|
|
zcode_len = notice->z_authent_len * 2 + 2; /* 2x growth plus Z and null */
|
|
notice->z_ascii_authent = (char *)malloc(zcode_len);
|
|
if (notice->z_ascii_authent == NULL)
|
|
result = ENOMEM;
|
|
}
|
|
if (!result)
|
|
result = ZMakeZcode(notice->z_ascii_authent, zcode_len,
|
|
(unsigned char *)authent->data, notice->z_authent_len);
|
|
|
|
/* format the notice header, with a zero checksum */
|
|
if (!result)
|
|
result = Z_NewFormatRawHeader(notice, buffer, buffer_len, phdr_len,
|
|
&cksum_start, &cksum_len, &cstart, &cend);
|
|
notice->z_authent_len = 0;
|
|
if (!result)
|
|
result = Z_InsertZcodeChecksum(keyblock, notice, buffer, cksum_start,
|
|
cksum_len, cstart, cend, buffer_len,
|
|
&phdr_adj, 0);
|
|
if (!result)
|
|
*phdr_len += phdr_adj;
|
|
|
|
if (notice->z_ascii_authent != NULL)
|
|
free(notice->z_ascii_authent);
|
|
krb5_free_data_contents(Z_krb5_ctx, authent);
|
|
if (authent != NULL)
|
|
free(authent);
|
|
return result;
|
|
}
|
|
|
|
int
|
|
ZGetCreds(krb5_creds **creds_out)
|
|
{
|
|
return ZGetCredsRealm(creds_out, __Zephyr_realm);
|
|
}
|
|
|
|
int
|
|
ZGetCredsRealm(krb5_creds **creds_out,
|
|
char *realm)
|
|
{
|
|
krb5_creds creds_in;
|
|
krb5_creds creds_tmp;
|
|
krb5_ccache ccache; /* XXX make this a global or static?*/
|
|
int result;
|
|
|
|
result = krb5_cc_default(Z_krb5_ctx, &ccache);
|
|
if (result)
|
|
return result;
|
|
|
|
memset((char *)&creds_in, 0, sizeof(creds_in));
|
|
result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
|
|
strlen(realm),
|
|
realm,
|
|
SERVER_SERVICE, SERVER_INSTANCE,
|
|
NULL);
|
|
if (result) {
|
|
krb5_cc_close(Z_krb5_ctx, ccache);
|
|
return result;
|
|
}
|
|
|
|
result = krb5_cc_get_principal(Z_krb5_ctx, ccache, &creds_in.client);
|
|
if (!result) {
|
|
result = krb5_cc_retrieve_cred(Z_krb5_ctx, ccache,
|
|
#ifdef KRB5_TC_SUPPORTED_KTYPES
|
|
KRB5_TC_SUPPORTED_KTYPES, /* MIT */
|
|
#else
|
|
0, /* Heimdal or other Space KRB5 */
|
|
#endif
|
|
&creds_in, &creds_tmp);
|
|
if (!result) {
|
|
*creds_out = malloc(sizeof(creds_tmp));
|
|
if (*creds_out == NULL)
|
|
result = errno;
|
|
else
|
|
memcpy(*creds_out, &creds_tmp, sizeof(creds_tmp));
|
|
}
|
|
}
|
|
if (result == KRB5_CC_NOTFOUND || result == KRB5_CC_END)
|
|
result = krb5_get_credentials(Z_krb5_ctx, 0, ccache, &creds_in, creds_out);
|
|
|
|
krb5_cc_close(Z_krb5_ctx, ccache);
|
|
krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* I also hope this is ok */
|
|
|
|
return result;
|
|
}
|
|
#endif
|