Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5035,6 +5035,7 @@ AC_ARG_WITH([kerberos5],
AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h])
AC_CHECK_HEADERS([gssapi_krb5.h gssapi/gssapi_krb5.h])
AC_CHECK_HEADERS([gssapi_generic.h gssapi/gssapi_generic.h])
AC_CHECK_HEADERS([gssapi_ext.h gssapi/gssapi_ext.h])

AC_SEARCH_LIBS([k_hasafs], [kafs], [AC_DEFINE([USE_AFS], [1],
[Define this if you want to use libkafs' AFS support])])
Expand Down
88 changes: 81 additions & 7 deletions gss-serv-krb5.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "log.h"
#include "misc.h"
#include "servconf.h"
#include "match.h"

#include "ssh-gss.h"

Expand Down Expand Up @@ -76,6 +77,34 @@ ssh_gssapi_krb5_init(void)
return 1;
}

/* Check if any of the indicators in the Kerberos ticket match
* one of indicators in the list of allowed/denied rules.
* In case of the match, apply the decision from the rule.
* In case of no indicator from the ticket matching the rule, deny
*/

static int
ssh_gssapi_check_indicators(ssh_gssapi_client *client, int *matched)
{
int ret;
u_int i;

*matched = -1;

/* Check indicators */
for (i = 0; client->indicators[i] != NULL; i++) {
ret = match_pattern_list(client->indicators[i],
options.gss_indicators, 1);
/* negative or positive match */
if (ret != 0) {
*matched = i;
return ret;
}
}
/* No rule matched */
return 0;
}

/* Check if this user is OK to login. This only works with krb5 - other
* GSSAPI mechanisms will need their own.
* Returns true if the user is OK to log in, otherwise returns 0
Expand All @@ -85,26 +114,71 @@ static int
ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name)
{
krb5_principal princ;
int retval;
int retval, matched, success;
const char *errmsg;

if (ssh_gssapi_krb5_init() == 0)
return 0;

if ((retval = krb5_parse_name(krb_context, client->exportedname.value,
&princ))) {
retval = krb5_parse_name(krb_context, client->exportedname.value, &princ);
if (retval) {
errmsg = krb5_get_error_message(krb_context, retval);
logit("krb5_parse_name(): %.100s", errmsg);
krb5_free_error_message(krb_context, errmsg);
return 0;
}
if (krb5_kuserok(krb_context, princ, name)) {

/* Let the Kerberos library to map the principal to POSIX account */
success = krb5_kuserok(krb_context, princ, name);
if (success == FALSE) {
retval = 0;
goto out;
}

/* At this point we are good if no indicators were defined */
if (options.gss_indicators == NULL) {
retval = 1;
logit("Authorized to %s, krb5 principal %s (krb5_kuserok)",
name, (char *)client->displayname.value);
} else
goto out;
}

/* At this point we have indicators defined in the configuration,
* if clientt did not provide any indicators, we reject */
if (!client->indicators) {
retval = 0;
logit("GSSAPI authentication indicators enforced "
"but indicators not provided by the client. "
"krb5 principal %s denied",
(char *)client->displayname.value);
goto out;
}

/* At this point the configuration enforces presence of indicators
* check the match */
matched = -1;
success = ssh_gssapi_check_indicators(client, &matched);

switch (success) {
case 1:
logit("Provided indicator %s allowed by the configuration",
client->indicators[matched]);
retval = 1;
break;
case -1:
logit("Provided indicator %s rejected by the configuration",
client->indicators[matched]);
retval = 0;
break;
default:
logit("Provided indicators do not match the configuration");
retval = 0;
break;
}

out:
if (retval == 1) {
logit("Authorized to %s, krb5 principal %s (krb5_userok)",
name, (char *)client->displayname.value);
}
krb5_free_principal(krb_context, princ);
return retval;
}
Expand Down
94 changes: 90 additions & 4 deletions gss-serv.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extern ServerOptions options;

static ssh_gssapi_client gssapi_client =
{ GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}, NULL};

ssh_gssapi_mech gssapi_null_mech =
{ NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
Expand Down Expand Up @@ -269,6 +269,78 @@ ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
return GSS_S_COMPLETE;
}


/* Extract authentication indicators from the Kerberos ticket. Authentication
* indicators are GSSAPI name attributes for the name "auth-indicators".
* Multiple indicators might be present in the ticket.
* Each indicator is an utf8 string. */

#define AUTH_INDICATORS_TAG "auth-indicators"

/* Privileged (called from accept_secure_ctx) */
static OM_uint32
ssh_gssapi_getindicators(Gssctxt *ctx, gss_name_t gss_name, ssh_gssapi_client *client)
{
gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
gss_buffer_desc display_value = GSS_C_EMPTY_BUFFER;
int is_mechname, authenticated, complete, more;
size_t count, i;

ctx->major = gss_inquire_name(&ctx->minor, gss_name,
&is_mechname, NULL, &attrs);
if (ctx->major != GSS_S_COMPLETE) {
return (ctx->major);
}

if (attrs == GSS_C_NO_BUFFER_SET) {
/* No indicators in the ticket */
return (0);
}

client->indicators = NULL;
count = 0;
for (i = 0; i < attrs->count; i++) {
authenticated = 0;
complete = 0;
more = -1;
/* skip anything but auth-indicators */
if (((sizeof(AUTH_INDICATORS_TAG) - 1) != attrs->elements[i].length) ||
memcmp(AUTH_INDICATORS_TAG,
attrs->elements[i].value,
sizeof(AUTH_INDICATORS_TAG) - 1) != 0)
continue;
/* retrieve all indicators */
while (more != 0) {
value.value = NULL;
display_value.value = NULL;
ctx->major = gss_get_name_attribute(&ctx->minor, gss_name,
&attrs->elements[i], &authenticated,
&complete, &value, &display_value, &more);
if (ctx->major != GSS_S_COMPLETE)
goto out;

if ((value.value != NULL) && authenticated) {
client->indicators = xrecallocarray(client->indicators, count, count + 1, sizeof(char*));
if (client->indicators == NULL) {
fatal("ssh_gssapi_getindicators failed to allocate memory");
}
client->indicators[count] = xmalloc(value.length + 1);
memcpy(client->indicators[count], value.value, value.length);
client->indicators[count][value.length] = '\0';
count++;
}
}
}

out:
(void) gss_release_buffer(&ctx->minor, &value);
(void) gss_release_buffer(&ctx->minor, &display_value);
(void) gss_release_buffer_set(&ctx->minor, &attrs);
return (ctx->major);
}


/* Extract the client details from a given context. This can only reliably
* be called once for a context */

Expand Down Expand Up @@ -310,6 +382,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
return (ctx->major);
}

/* Retrieve authentication indicators, if they exist */
if ((ctx->major = ssh_gssapi_getindicators(ctx,
ctx->client, client))) {
ssh_gssapi_error(ctx);
return (ctx->major);
}

/* We can't copy this structure, so we just move the pointer to it */
client->creds = ctx->client_creds;
ctx->client_creds = GSS_C_NO_CREDENTIAL;
Expand All @@ -334,7 +413,7 @@ ssh_gssapi_storecreds(void)
{
if (options.gss_deleg_creds == 0) {
debug_f("delegate credential is disabled, doing nothing");
return 0;
return;
}

if (gssapi_client.mech && gssapi_client.mech->storecreds) {
Expand Down Expand Up @@ -365,6 +444,7 @@ int
ssh_gssapi_userok(char *user)
{
OM_uint32 lmin;
size_t i;

if (gssapi_client.exportedname.length == 0 ||
gssapi_client.exportedname.value == NULL) {
Expand All @@ -379,8 +459,14 @@ ssh_gssapi_userok(char *user)
gss_release_buffer(&lmin, &gssapi_client.displayname);
gss_release_buffer(&lmin, &gssapi_client.exportedname);
gss_release_cred(&lmin, &gssapi_client.creds);
explicit_bzero(&gssapi_client,
sizeof(ssh_gssapi_client));

if (gssapi_client.indicators != NULL) {
for (i = 0; gssapi_client.indicators[i] != NULL; i++)
free(gssapi_client.indicators[i]);
free(gssapi_client.indicators);
}

explicit_bzero(&gssapi_client, sizeof(ssh_gssapi_client));
return 0;
}
else
Expand Down
15 changes: 15 additions & 0 deletions servconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ initialize_server_options(ServerOptions *options)
options->gss_cleanup_creds = -1;
options->gss_deleg_creds = -1;
options->gss_strict_acceptor = -1;
options->gss_indicators = NULL;
options->password_authentication = -1;
options->kbd_interactive_authentication = -1;
options->permit_empty_passwd = -1;
Expand Down Expand Up @@ -531,6 +532,7 @@ fill_default_server_options(ServerOptions *options)
CLEAR_ON_NONE(options->routing_domain);
CLEAR_ON_NONE(options->host_key_agent);
CLEAR_ON_NONE(options->per_source_penalty_exempt);
CLEAR_ON_NONE(options->gss_indicators);

for (i = 0; i < options->num_host_key_files; i++)
CLEAR_ON_NONE(options->host_key_files[i]);
Expand Down Expand Up @@ -568,6 +570,7 @@ typedef enum {
sPerSourcePenalties, sPerSourcePenaltyExemptList,
sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
sGssAuthentication, sGssCleanupCreds, sGssDelegateCreds, sGssStrictAcceptor,
sGssIndicators,
sAcceptEnv, sSetEnv, sPermitTunnel,
sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
sUsePrivilegeSeparation, sAllowAgentForwarding,
Expand Down Expand Up @@ -655,11 +658,13 @@ static struct {
{ "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
{ "gssapidelegatecredentials", sGssDelegateCreds, SSHCFG_GLOBAL },
{ "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
{ "gssapiindicators", sGssIndicators, SSHCFG_ALL },
#else
{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
{ "gssapidelegatecredentials", sUnsupported, SSHCFG_GLOBAL },
{ "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
{ "gssapiindicators", sUnsupported, SSHCFG_ALL },
#endif
{ "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
Expand Down Expand Up @@ -1665,6 +1670,15 @@ process_server_config_line_depth(ServerOptions *options, char *line,
intptr = &options->gss_strict_acceptor;
goto parse_flag;

case sGssIndicators:
arg = argv_next(&ac, &av);
if (!arg || *arg == '\0')
fatal("%s line %d: %s missing argument.",
filename, linenum, keyword);
if (options->gss_indicators == NULL)
options->gss_indicators = xstrdup(arg);
break;

case sPasswordAuthentication:
intptr = &options->password_authentication;
goto parse_flag;
Expand Down Expand Up @@ -3286,6 +3300,7 @@ dump_config(ServerOptions *o)
dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
dump_cfg_fmtint(sGssDelegateCreds, o->gss_deleg_creds);
dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
dump_cfg_string(sGssIndicators, o->gss_indicators);
#endif
dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
dump_cfg_fmtint(sKbdInteractiveAuthentication,
Expand Down
2 changes: 2 additions & 0 deletions servconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ typedef struct {
char **allow_groups;
u_int num_deny_groups;
char **deny_groups;
char *gss_indicators;

u_int num_subsystems;
char **subsystem_name;
Expand Down Expand Up @@ -303,6 +304,7 @@ TAILQ_HEAD(include_list, include_item);
M_CP_STROPT(routing_domain); \
M_CP_STROPT(permit_user_env_allowlist); \
M_CP_STROPT(pam_service_name); \
M_CP_STROPT(gss_indicators); \
M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \
M_CP_STRARRAYOPT(allow_users, num_allow_users); \
M_CP_STRARRAYOPT(deny_users, num_deny_users); \
Expand Down
7 changes: 7 additions & 0 deletions ssh-gss.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
#include <gssapi/gssapi.h>
#endif

#ifdef HAVE_GSSAPI_EXT_H
#include <gssapi_ext.h>
#elif defined(HAVE_GSSAPI_GSSAPI_EXT_H)
#include <gssapi/gssapi_ext.h>
#endif

#ifdef KRB5
# ifndef HEIMDAL
# ifdef HAVE_GSSAPI_GENERIC_H
Expand Down Expand Up @@ -74,6 +80,7 @@ typedef struct {
gss_cred_id_t creds;
struct ssh_gssapi_mech_struct *mech;
ssh_gssapi_ccache store;
char **indicators; /* auth indicators */
} ssh_gssapi_client;

typedef struct ssh_gssapi_mech_struct {
Expand Down
Loading