-/* $OpenBSD: auth2-gss.c,v 1.3 2003/09/01 20:44:54 markus Exp $ */
+/* $OpenBSD: auth2-gss.c,v 1.16 2007/10/29 00:52:45 dtucker Exp $ */
/*
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#ifdef GSSAPI
+#include <sys/types.h>
+
+#include <stdarg.h>
+
+#include "xmalloc.h"
+#include "key.h"
+#include "hostfile.h"
#include "auth.h"
#include "ssh2.h"
-#include "xmalloc.h"
#include "log.h"
#include "dispatch.h"
+#include "buffer.h"
#include "servconf.h"
-#include "compat.h"
#include "packet.h"
-#include "monitor_wrap.h"
-
#include "ssh-gss.h"
+#include "monitor_wrap.h"
extern ServerOptions options;
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
+static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_errtok(int, u_int32_t, void *);
+
+static int gssapi_with_mic = 1; /* flag to toggle "gssapi-with-mic" vs.
+ "gssapi" */
+
static int
userauth_external(Authctxt *authctxt)
{
packet_check_eom();
- return(PRIVSEP(ssh_gssapi_userok(authctxt->user)));
+ if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+ return(PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)));
+ }
+ return 0;
}
-static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
-static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
-static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
-static void input_gssapi_errtok(int, u_int32_t, void *);
+/*
+ * The 'gssapi_keyex' userauth mechanism.
+ */
+static int
+userauth_gsskeyex(Authctxt *authctxt)
+{
+ int authenticated = 0;
+ Buffer b, b2;
+ gss_buffer_desc mic, gssbuf, gssbuf2;
+ u_int len;
+
+ mic.value = packet_get_string(&len);
+ mic.length = len;
+
+ packet_check_eom();
+
+ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
+ "gssapi-keyex");
+
+ gssbuf.value = buffer_ptr(&b);
+ gssbuf.length = buffer_len(&b);
+
+ /* client may have used empty username to determine target
+ name from GSSAPI context */
+ ssh_gssapi_buildmic(&b2, "", authctxt->service, "gssapi-keyex");
+
+ gssbuf2.value = buffer_ptr(&b2);
+ gssbuf2.length = buffer_len(&b2);
+
+ /* gss_kex_context is NULL with privsep, so we can't check it here */
+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
+ &gssbuf, &mic))) ||
+ !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
+ &gssbuf2, &mic)))) {
+ if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+ authctxt->pw));
+ }
+ }
+
+ buffer_free(&b);
+ buffer_free(&b2);
+ xfree(mic.value);
+
+ return (authenticated);
+}
/*
* We only support those mechanisms that we know about (ie ones that we know
- * how to check local user kuserok and the like
+ * how to check local user kuserok and the like)
*/
static int
userauth_gssapi(Authctxt *authctxt)
{
- gss_OID_desc oid = {0, NULL};
+ gss_OID_desc goid = {0, NULL};
Gssctxt *ctxt = NULL;
int mechs;
gss_OID_set supported;
int present;
OM_uint32 ms;
u_int len;
- char *doid = NULL;
+ u_char *doid = NULL;
+
+ /* authctxt->valid may be 0 if we haven't yet determined
+ username from gssapi context. */
- if (!authctxt->valid || authctxt->user == NULL)
+ if (authctxt->user == NULL)
return (0);
mechs = packet_get_int();
if (doid)
xfree(doid);
+ present = 0;
doid = packet_get_string(&len);
- if (doid[0] != SSH_GSS_OIDTYPE || doid[1] != len-2) {
- logit("Mechanism OID received using the old encoding form");
- oid.elements = doid;
- oid.length = len;
+ if (len > 2 && doid[0] == SSH_GSS_OIDTYPE &&
+ doid[1] == len - 2) {
+ goid.elements = doid + 2;
+ goid.length = len - 2;
+ gss_test_oid_set_member(&ms, &goid, supported,
+ &present);
} else {
- oid.elements = doid + 2;
- oid.length = len - 2;
+ logit("Badly formed OID received");
}
- gss_test_oid_set_member(&ms, &oid, supported, &present);
} while (mechs > 0 && !present);
gss_release_oid_set(&ms, &supported);
if (!present) {
xfree(doid);
+ authctxt->server_caused_failure = 1;
return (0);
}
- if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &oid)))) {
+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
+ if (ctxt != NULL)
+ ssh_gssapi_delete_ctx(&ctxt);
xfree(doid);
+ authctxt->server_caused_failure = 1;
return (0);
}
- authctxt->methoddata=(void *)ctxt;
+ authctxt->methoddata = (void *)ctxt;
packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
- /* Return OID in same format as we received it*/
+ /* Return the OID that we received */
packet_put_string(doid, len);
packet_send();
Gssctxt *gssctxt;
gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
gss_buffer_desc recv_tok;
- OM_uint32 maj_status, min_status;
+ OM_uint32 maj_status, min_status, flags=0;
u_int len;
if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
packet_check_eom();
maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
- &send_tok, NULL));
+ &send_tok, &flags));
xfree(recv_tok.value);
}
authctxt->postponed = 0;
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
- userauth_finish(authctxt, 0, "gssapi");
+ userauth_finish(authctxt, 0,
+ gssapi_with_mic ? "gssapi-with-mic" :
+ "gssapi");
} else {
if (send_tok.length != 0) {
packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
}
if (maj_status == GSS_S_COMPLETE) {
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
- dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
- &input_gssapi_exchange_complete);
+ if (flags & GSS_C_INTEG_FLAG && gssapi_with_mic)
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC,
+ &input_gssapi_mic);
+ else
+ dispatch_set(
+ SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
+ &input_gssapi_exchange_complete);
}
}
gss_release_buffer(&maj_status, &send_tok);
}
+static void
+gssapi_set_username(Authctxt *authctxt)
+{
+ char *lname = NULL;
+
+ if ((authctxt->user == NULL) || (authctxt->user[0] == '\0')) {
+ PRIVSEP(ssh_gssapi_localname(&lname));
+ if (lname && lname[0] != '\0') {
+ if (authctxt->user) xfree(authctxt->user);
+ authctxt->user = lname;
+ debug("set username to %s from gssapi context", lname);
+ authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user));
+ if (authctxt->pw) {
+ authctxt->valid = 1;
+#ifdef USE_PAM
+ if (options.use_pam)
+ PRIVSEP(start_pam(authctxt));
+#endif
+ }
+ } else {
+ debug("failed to set username from gssapi context");
+ packet_send_debug("failed to set username from gssapi context");
+ }
+ }
+}
+
/*
* This is called when the client thinks we've completed authentication.
* It should only be enabled in the dispatch handler by the function above,
if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
fatal("No authentication or GSSAPI context");
- if ((strcmp(authctxt->user, "") == 0) && (authctxt->pw == NULL)) {
- char *lname = NULL;
- PRIVSEP(ssh_gssapi_localname(&lname));
- if (lname && lname[0] != '\0') {
- xfree(authctxt->user);
- authctxt->user = lname;
- debug("set username to %s from gssapi context", lname);
- authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user));
- } else {
- debug("failed to set username from gssapi context");
- }
- }
- if (authctxt->pw) {
-#ifdef USE_PAM
- PRIVSEP(start_pam(authctxt->pw->pw_name));
-#endif
- } else {
- authctxt->valid = 0;
- authenticated = 0;
- goto finish;
- }
+ gssapi_set_username(authctxt);
gssctxt = authctxt->methoddata;
/*
- * We don't need to check the status, because the stored credentials
- * which userok uses are only populated once the context init step
- * has returned complete.
+ * We don't need to check the status, because we're only enabled in
+ * the dispatcher once the exchange is complete
*/
packet_check_eom();
- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
+ /* user should be set if valid but we double-check here */
+ if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+ authctxt->pw));
+ } else {
+ authenticated = 0;
+ }
+
+ authctxt->postponed = 0;
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+ userauth_finish(authctxt, authenticated,
+ gssapi_with_mic ? "gssapi-with-mic" : "gssapi");
+}
+
+static int
+userauth_gssapi_with_mic(Authctxt *authctxt)
+{
+ gssapi_with_mic = 1;
+ return userauth_gssapi(authctxt);
+}
+
+static int
+userauth_gssapi_without_mic(Authctxt *authctxt)
+{
+ gssapi_with_mic = 0;
+ return userauth_gssapi(authctxt);
+}
+
+static void
+input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ int authenticated = 0;
+ Buffer b;
+ gss_buffer_desc mic, gssbuf;
+ u_int len;
+
+ if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+ fatal("No authentication or GSSAPI context");
+
+ gssctxt = authctxt->methoddata;
+
+ mic.value = packet_get_string(&len);
+ mic.length = len;
+
+ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
+ "gssapi-with-mic");
+
+ gssbuf.value = buffer_ptr(&b);
+ gssbuf.length = buffer_len(&b);
+
+ gssapi_set_username(authctxt);
+
+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
+ if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+ authenticated =
+ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
+ } else {
+ authenticated = 0;
+ }
+ else
+ logit("GSSAPI MIC check failed");
+
+ buffer_free(&b);
+ xfree(mic.value);
-finish:
authctxt->postponed = 0;
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
- userauth_finish(authctxt, authenticated, "gssapi");
+ userauth_finish(authctxt, authenticated, "gssapi-with-mic");
}
static void ssh_gssapi_userauth_error(Gssctxt *ctxt) {
&options.gss_authentication
};
+Authmethod method_gsskeyex = {
+ "gssapi-keyex",
+ userauth_gsskeyex,
+ &options.gss_authentication
+};
+
Authmethod method_gssapi = {
+ "gssapi-with-mic",
+ userauth_gssapi_with_mic,
+ &options.gss_authentication
+};
+
+Authmethod method_gssapi_compat = {
"gssapi",
- userauth_gssapi,
+ userauth_gssapi_without_mic,
&options.gss_authentication
};