/*
* GSSAPI Module
*
* Copyright (c) 2005 Jelmer Vernooij <jelmer@samba.org>
*
* Related standards:
* - draft-ietf-sasl-gssapi-03
* - RFC2222
*
* Some parts inspired by an older patch from Colin Walters
*
* This software is released under the MIT license.
*/
#include "auth-common.h"
#include "env-util.h"
#include "str.h"
#include "str-sanitize.h"
#include "hex-binary.h"
#include "safe-memset.h"
#include "mech.h"
#include "passdb.h"
#if defined(BUILTIN_GSSAPI) || defined(PLUGIN_BUILD)
#ifndef HAVE___GSS_USEROK
# define USE_KRB5_USEROK
# include <krb5.h>
#endif
#ifdef HAVE_GSSAPI_GSSAPI_H
#elif defined (HAVE_GSSAPI_H)
# include <gssapi.h>
#endif
#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
# include <gssapi/gssapi_krb5.h>
#elif defined (HAVE_GSSAPI_KRB5_H)
# include <gssapi_krb5.h>
#else
#endif
#ifdef HAVE_GSSAPI_GSSAPI_EXT_H
# include <gssapi/gssapi_ext.h>
#endif
#define krb5_boolean2bool(X) ((X) != 0)
/* Non-zero flags defined in RFC 2222 */
enum sasl_gssapi_qop {
};
struct gssapi_auth_request {
enum {
};
{ 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
static int
const char *description)
{
do {
"While %s: %s", description,
} while (message_context != 0);
}
{
if (*path != '\0') {
/* environment may be used by Kerberos 5 library directly */
#elif defined (HAVE_KRB5_GSS_REGISTER_ACCEPTOR_IDENTITY)
#endif
}
}
{
return &request->auth_request;
}
static OM_uint32
{
const char *service_name;
if (!gssapi_initialized) {
}
"Using all keytab entries");
return GSS_S_COMPLETE;
}
/* The standard POP3 service name with GSSAPI is called
just "pop". */
service_name = "pop";
} else {
}
if (GSS_ERROR(major_status) != 0) {
"importing principal name");
return major_status;
}
if (GSS_ERROR(major_status) != 0) {
"acquiring service credentials");
"acquiring service credentials");
return major_status;
}
return major_status;
}
static gss_name_t
{
GSS_C_NO_OID, &name);
if (GSS_ERROR(major_status) != 0) {
"gss_import_name");
return GSS_C_NO_NAME;
}
return name;
}
static gss_name_t
{
if (GSS_ERROR(major_status) != 0) {
"gss_duplicate_name");
return GSS_C_NO_NAME;
}
return new;
}
{
const unsigned char *c = data;
size_t i;
/* apparently all names end with NUL? */
len--;
for (i = 0; i < len; i++) {
if (c[i] == '\0')
return TRUE;
}
return FALSE;
}
{
&buf, name_type_r);
if (major_status != GSS_S_COMPLETE) {
GSS_C_GSS_CODE, "gss_display_name");
return -1;
}
"authn_name has NULs");
return -1;
}
return 0;
}
const gss_OID_desc *oid2)
{
}
static int
{
int ret = 0;
&inbuf,
NULL, /* ret_flags */
NULL, /* time_rec */
NULL /* delegated_cred_handle */
);
if (GSS_ERROR(major_status) != 0) {
"processing incoming data");
"processing incoming data");
return -1;
}
switch (major_status) {
case GSS_S_COMPLETE:
"GSSAPI mechanism not Kerberos5");
ret = -1;
ret = -1;
&error)) {
"authn_name: %s", error);
ret = -1;
} else {
"security context state completed.");
}
break;
case GSS_S_CONTINUE_NEEDED:
"Processed incoming packet correctly, "
"waiting for another.");
break;
default:
"Received unexpected major status %d", major_status);
break;
}
if (ret == 0) {
if (output_token.length > 0) {
} else {
/* If there is no output token, go straight to wrap,
which is expecting an empty input token. */
}
}
return ret;
}
static int
{
/* The client's return data should be empty here */
/* Only authentication, no integrity or confidentiality
protection (yet?) */
ret[0] = (SASL_GSSAPI_QOP_UNSPECIFIED |
if (GSS_ERROR(major_status) != 0) {
GSS_C_GSS_CODE, "sending security layer negotiation");
GSS_C_MECH_CODE, "sending security layer negotiation");
return -1;
}
"Negotiated security layer");
return 0;
}
#ifdef USE_KRB5_USEROK
static bool
{
return FALSE;
"authorized by k5principals field: %s", name);
return TRUE;
}
}
return FALSE;
}
static bool
bool check_name_type)
{
const char *princ_display_name;
/* Parse out the principal's username */
&princ_display_name) < 0)
return FALSE;
"OID not kerberos principal name");
return FALSE;
}
/* Init a krb5 context and parse the principal username */
if (krb5_err != 0) {
"krb5_init_context() failed: %d", (int)krb5_err);
return FALSE;
}
if (krb5_err != 0) {
/* writing the error string would be better, but we probably
rarely get here and there doesn't seem to be a standard
way of getting it */
"krb5_parse_name() failed: %d",
(int)krb5_err);
} else {
/* See if the principal is in the list of authorized
* principals for the user */
/* See if the principal is authorized to act as the
specified (UNIX) user */
if (!authorized) {
}
}
return authorized;
}
#endif
static int
{
int equal_authn_authz;
#ifdef HAVE___GSS_USEROK
int login_ok;
#endif
/* if authn and authz names equal, don't bother checking further. */
if (GSS_ERROR(major_status) != 0) {
"gss_compare_name failed");
return -1;
}
if (equal_authn_authz != 0)
return 0;
/* handle cross-realm authentication */
#ifdef HAVE___GSS_USEROK
/* Solaris */
login_user, &login_ok);
if (GSS_ERROR(major_status) != 0) {
GSS_C_GSS_CODE, "__gss_userok failed");
return -1;
}
if (login_ok == 0) {
"User not authorized to log in as %s", login_user);
return -1;
}
return 0;
#elif defined(USE_KRB5_USEROK)
login_user, TRUE)) {
"User not authorized to log in as %s", login_user);
return -1;
}
return 0;
#else
"Cross-realm authentication not supported "
return -1;
#endif
}
static void
const unsigned char *credentials ATTR_UNUSED,
struct auth_request *request)
{
(struct gssapi_auth_request *)request;
/* We don't care much whether the lookup succeeded or not because GSSAPI
* does not strictly require a passdb. But if a passdb is configured,
* now the k5principals field will have been filled in. */
switch (result) {
return;
/* user is explicitly disabled, don't allow it to log in */
return;
case PASSDB_RESULT_NEXT:
case PASSDB_RESULT_OK:
break;
}
else
}
static int
{
unsigned char *name;
if (GSS_ERROR(major_status) != 0) {
"final negotiation: gss_unwrap");
return -1;
}
/* outbuf[0] contains bitmask for selected security layer,
outbuf[1..3] contains maximum output_message size */
"Invalid response length");
return -1;
}
"authz_name has NULs");
return -1;
}
} else {
NULL, &login_user) < 0)
return -1;
}
"no authz_name");
return -1;
}
/* Set username early, so that the credential lookup is for the
* authorizing user. This means the username in subsequent log
* messages will be the authorization name, not the authentication
* name, which may mean that future log messages should be adjusted
* to log the right thing. */
"authz_name: %s", error);
return -1;
}
/* Continue in callback once auth_request is populated with passdb
information. */
return 0;
}
static void
{
(struct gssapi_auth_request *)request;
switch (gssapi_request->sasl_gssapi_state) {
case GSS_STATE_SEC_CONTEXT:
break;
case GSS_STATE_WRAP:
break;
case GSS_STATE_UNWRAP:
break;
default:
i_unreached();
}
if (ret < 0)
}
static void
{
(struct gssapi_auth_request *)request;
if (GSS_ERROR(major_status) != 0) {
return;
}
if (data_size == 0) {
/* The client should go first */
} else {
}
}
static void
{
(struct gssapi_auth_request *)request;
(void)gss_delete_sec_context(&minor_status,
}
(void)gss_release_name(&minor_status,
}
(void)gss_release_name(&minor_status,
}
}
"GSSAPI",
.flags = 0,
};
/* MTI Kerberos v1.5+ and Heimdal v0.7+ supports SPNEGO for Kerberos tickets
internally. Nothing else needs to be done here. Note however that this does
not support SPNEGO when the only available credential is NTLM.. */
"GSS-SPNEGO",
.flags = 0,
};
#ifndef BUILTIN_GSSAPI
void mech_gssapi_init(void);
void mech_gssapi_deinit(void);
void mech_gssapi_init(void)
{
#ifdef HAVE_GSSAPI_SPNEGO
/* load if we already didn't load it using winbind */
#endif
}
void mech_gssapi_deinit(void)
{
#ifdef HAVE_GSSAPI_SPNEGO
#endif
}
#endif
#endif