diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c index 4caac337c..cebb334ca 100644 --- a/gss-serv-krb5.c +++ b/gss-serv-krb5.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gss-serv-krb5.c,v 1.10 2026/02/08 15:28:01 dtucker Exp $ */ +/* $OpenBSD: gss-serv-krb5.c,v 1.11 2026/10/30 04:52:11 djm Exp $ */ /* * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. @@ -30,8 +30,11 @@ #ifdef KRB5 #include +#include +#include #include +#include #include #include "xmalloc.h" @@ -76,13 +79,53 @@ ssh_gssapi_krb5_init(void) return 1; } +/* + * krb5_kuserok() consults the user's ~/.k5login but may check no more than + * its owner, so when StrictModes is set subject it and the home directory + * to the same ownership and mode checks as the user's other access lists + * (authorized_keys, .shosts, etc). + * Returns true if they are safe to use, otherwise returns 0 + */ + +static int +ssh_gssapi_krb5_k5login_ok(struct passwd *pw) +{ + struct stat st; + char *path, err[1024]; + int ok = 1; + + if (!options.strict_modes) + return 1; + + /* + * Check the home directory first: anyone who can write to it can + * supply a .k5login, and safe_path() does not see the directory a + * symlink was planted in, only the one its target lives in. + */ + if (stat(pw->pw_dir, &st) == 0 && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + logit("Authentication refused for %.100s: bad ownership or " + "modes for home directory %.200s", pw->pw_name, pw->pw_dir); + return 0; + } + xasprintf(&path, "%s/.k5login", pw->pw_dir); + if (stat(path, &st) == 0 && safe_path(path, &st, pw->pw_dir, + pw->pw_uid, err, sizeof(err)) != 0) { + logit("Authentication refused: %s", err); + ok = 0; + } + free(path); + return ok; +} + /* 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 */ static int -ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) +ssh_gssapi_krb5_userok(ssh_gssapi_client *client, struct passwd *pw) { krb5_principal princ; int retval; @@ -91,6 +134,9 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) if (ssh_gssapi_krb5_init() == 0) return 0; + if (!ssh_gssapi_krb5_k5login_ok(pw)) + return 0; + if ((retval = krb5_parse_name(krb_context, client->exportedname.value, &princ))) { errmsg = krb5_get_error_message(krb_context, retval); @@ -98,10 +144,10 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) krb5_free_error_message(krb_context, errmsg); return 0; } - if (krb5_kuserok(krb_context, princ, name)) { + if (krb5_kuserok(krb_context, princ, pw->pw_name)) { retval = 1; logit("Authorized to %s, krb5 principal %s (krb5_kuserok)", - name, (char *)client->displayname.value); + pw->pw_name, (char *)client->displayname.value); } else retval = 0; diff --git a/gss-serv.c b/gss-serv.c index 32eaa4de0..d25eaf400 100644 --- a/gss-serv.c +++ b/gss-serv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gss-serv.c,v 1.40 2026/10/07 09:00:11 robert Exp $ */ +/* $OpenBSD: gss-serv.c,v 1.41 2026/10/30 04:52:11 djm Exp $ */ /* * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. @@ -380,7 +380,7 @@ ssh_gssapi_cleanup_global_client(void) /* Privileged */ int -ssh_gssapi_userok(char *user) +ssh_gssapi_userok(struct passwd *pw) { if (gssapi_client.exportedname.length == 0 || gssapi_client.exportedname.value == NULL) { @@ -388,7 +388,7 @@ ssh_gssapi_userok(char *user) return 0; } if (gssapi_client.mech && gssapi_client.mech->userok) - if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { + if ((*gssapi_client.mech->userok)(&gssapi_client, pw)) { gssapi_client.userok = 1; return 1; } else { diff --git a/monitor.c b/monitor.c index f2890d26c..ebcd9c8c8 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.262 2026/10/30 04:40:03 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.263 2026/10/30 04:52:11 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -2096,7 +2096,7 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) if (!options.gss_authentication) fatal_f("GSSAPI authentication not enabled"); - authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); + authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->pw); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, authenticated)) != 0) diff --git a/ssh-gss.h b/ssh-gss.h index d3cf70124..98fbd07fc 100644 --- a/ssh-gss.h +++ b/ssh-gss.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-gss.h,v 1.19 2026/10/07 09:00:11 robert Exp $ */ +/* $OpenBSD: ssh-gss.h,v 1.20 2026/10/30 04:52:11 djm Exp $ */ /* * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. * @@ -61,6 +61,8 @@ #define SSH_GSS_OIDTYPE 0x06 +struct passwd; + typedef struct { char *filename; char *envvar; @@ -82,7 +84,7 @@ typedef struct ssh_gssapi_mech_struct { char *name; gss_OID_desc oid; int (*dochild) (ssh_gssapi_client *); - int (*userok) (ssh_gssapi_client *, char *); + int (*userok) (ssh_gssapi_client *, struct passwd *); int (*localname) (ssh_gssapi_client *, char **); void (*storecreds) (ssh_gssapi_client *); } ssh_gssapi_mech; @@ -127,7 +129,7 @@ int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); /* In the server */ OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); -int ssh_gssapi_userok(char *name); +int ssh_gssapi_userok(struct passwd *); OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); void ssh_gssapi_do_child(char ***, u_int *); void ssh_gssapi_cleanup_creds(void);