*/
#include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.118 2003/05/14 02:15:47 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.122 2003/08/22 13:20:03 markus Exp $");
-#ifdef KRB5
-#include <krb5.h>
-#endif
+#include "openbsd-compat/sys-queue.h"
#include "ssh.h"
#include "ssh2.h"
#include "msg.h"
#include "pathnames.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
/* import */
extern char *client_version_string;
extern char *server_version_string;
*/
u_char *session_id2 = NULL;
-int session_id2_len = 0;
+u_int session_id2_len = 0;
char *xxx_host;
struct sockaddr *xxx_hostaddr;
Sensitive *sensitive;
/* kbd-interactive */
int info_req_seen;
+ /* generic */
+ void *methoddata;
};
struct Authmethod {
char *name; /* string to compare against server's list */
int userauth_hostbased(Authctxt *);
int userauth_kerberos(Authctxt *);
+#ifdef GSSAPI
+int userauth_gssapi(Authctxt *authctxt);
+void input_gssapi_response(int type, u_int32_t, void *);
+void input_gssapi_token(int type, u_int32_t, void *);
+void input_gssapi_hash(int type, u_int32_t, void *);
+void input_gssapi_error(int, u_int32_t, void *);
+void input_gssapi_errtok(int, u_int32_t, void *);
+#endif
+
void userauth(Authctxt *, char *);
static int sign_and_send_pubkey(Authctxt *, Identity *);
-static void clear_auth_state(Authctxt *);
static void pubkey_prepare(Authctxt *);
static void pubkey_cleanup(Authctxt *);
static Key *load_identity_file(char *);
static char *authmethods_get(void);
Authmethod authmethods[] = {
+#ifdef GSSAPI
+ {"gssapi",
+ userauth_gssapi,
+ &options.gss_authentication,
+ NULL},
+#endif
{"hostbased",
userauth_hostbased,
&options.hostbased_authentication,
NULL},
-#if KRB5
- {"kerberos-2@ssh.com",
- userauth_kerberos,
- &options.kerberos_authentication,
- NULL},
-#endif
{"publickey",
userauth_pubkey,
&options.pubkey_authentication,
authctxt.success = 0;
authctxt.method = authmethod_lookup("none");
authctxt.authlist = NULL;
+ authctxt.methoddata = NULL;
authctxt.sensitive = sensitive;
authctxt.info_req_seen = 0;
if (authctxt.method == NULL)
dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */
pubkey_cleanup(&authctxt);
+ dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
+
debug("Authentication succeeded (%s).", authctxt.method->name);
}
+
void
userauth(Authctxt *authctxt, char *authlist)
{
+ if (authctxt->methoddata) {
+ xfree(authctxt->methoddata);
+ authctxt->methoddata = NULL;
+ }
if (authlist == NULL) {
authlist = authctxt->authlist;
} else {
if (method == NULL)
fatal("Permission denied (%s).", authlist);
authctxt->method = method;
+
+ /* reset the per method handler */
+ dispatch_range(SSH2_MSG_USERAUTH_PER_METHOD_MIN,
+ SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL);
+
+ /* and try new method */
if (method->userauth(authctxt) != 0) {
debug2("we sent a %s packet, wait for reply", method->name);
break;
fatal("input_userauth_success: no authentication context");
if (authctxt->authlist)
xfree(authctxt->authlist);
- clear_auth_state(authctxt);
+ if (authctxt->methoddata)
+ xfree(authctxt->methoddata);
authctxt->success = 1; /* break out */
}
logit("Authenticated with partial success.");
debug("Authentications that can continue: %s", authlist);
- clear_auth_state(authctxt);
userauth(authctxt, authlist);
}
void
xfree(pkalg);
xfree(pkblob);
- /* unregister */
- clear_auth_state(authctxt);
- dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL);
-
/* try another method if we did not send a packet */
if (sent == 0)
userauth(authctxt, NULL);
}
+#ifdef GSSAPI
+int
+userauth_gssapi(Authctxt *authctxt)
+{
+ Gssctxt *gssctxt = NULL;
+ static gss_OID_set supported = NULL;
+ static int mech = 0;
+ OM_uint32 min;
+ int ok = 0;
+
+ /* Try one GSSAPI method at a time, rather than sending them all at
+ * once. */
+
+ if (supported == NULL)
+ gss_indicate_mechs(&min, &supported);
+
+ /* Check to see if the mechanism is usable before we offer it */
+ while (mech<supported->count && !ok) {
+ if (gssctxt)
+ ssh_gssapi_delete_ctx(&gssctxt);
+ ssh_gssapi_build_ctx(&gssctxt);
+ ssh_gssapi_set_oid(gssctxt, &supported->elements[mech]);
+
+ /* My DER encoding requires length<128 */
+ if (supported->elements[mech].length < 128 &&
+ !GSS_ERROR(ssh_gssapi_import_name(gssctxt,
+ authctxt->host))) {
+ ok = 1; /* Mechanism works */
+ } else {
+ mech++;
+ }
+ }
+
+ if (!ok) return 0;
+
+ authctxt->methoddata=(void *)gssctxt;
+
+ packet_start(SSH2_MSG_USERAUTH_REQUEST);
+ packet_put_cstring(authctxt->server_user);
+ packet_put_cstring(authctxt->service);
+ packet_put_cstring(authctxt->method->name);
+
+ packet_put_int(1);
+
+ /* Some servers encode the OID incorrectly (as we used to) */
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ packet_put_string(supported->elements[mech].elements,
+ supported->elements[mech].length);
+ } else {
+ packet_put_int((supported->elements[mech].length)+2);
+ packet_put_char(SSH_GSS_OIDTYPE);
+ packet_put_char(supported->elements[mech].length);
+ packet_put_raw(supported->elements[mech].elements,
+ supported->elements[mech].length);
+ }
+
+ packet_send();
+
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
+
+ mech++; /* Move along to next candidate */
+
+ return 1;
+}
+
+void
+input_gssapi_response(int type, u_int32_t plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ OM_uint32 status, ms;
+ int oidlen;
+ char *oidv;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+
+ if (authctxt == NULL)
+ fatal("input_gssapi_response: no authentication context");
+ gssctxt = authctxt->methoddata;
+
+ /* Setup our OID */
+ oidv = packet_get_string(&oidlen);
+
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ if (!ssh_gssapi_check_oid(gssctxt, oidv, oidlen))
+ fatal("Server returned different OID than expected");
+ } else {
+ if(oidv[0] != SSH_GSS_OIDTYPE || oidv[1] != oidlen-2) {
+ debug("Badly encoded mechanism OID received");
+ userauth(authctxt, NULL);
+ xfree(oidv);
+ return;
+ }
+ if (!ssh_gssapi_check_oid(gssctxt, oidv+2, oidlen-2))
+ fatal("Server returned different OID than expected");
+ }
+
+ packet_check_eom();
+
+ xfree(oidv);
+
+ status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+ GSS_C_NO_BUFFER, &send_tok, NULL);
+ if (GSS_ERROR(status)) {
+ if (send_tok.length > 0) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+ packet_put_string(send_tok.value, send_tok.length);
+ packet_send();
+ gss_release_buffer(&ms, &send_tok);
+ }
+ /* Start again with next method on list */
+ debug("Trying to start again");
+ userauth(authctxt, NULL);
+ return;
+ }
+
+ /* We must have data to send */
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+ packet_put_string(send_tok.value, send_tok.length);
+ packet_send();
+ gss_release_buffer(&ms, &send_tok);
+}
+
+void
+input_gssapi_token(int type, u_int32_t plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc recv_tok;
+ OM_uint32 status, ms;
+ u_int slen;
+
+ if (authctxt == NULL)
+ fatal("input_gssapi_response: no authentication context");
+ gssctxt = authctxt->methoddata;
+
+ recv_tok.value = packet_get_string(&slen);
+ recv_tok.length = slen; /* safe typecast */
+
+ packet_check_eom();
+
+ status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+ &recv_tok, &send_tok, NULL);
+
+ xfree(recv_tok.value);
+
+ if (GSS_ERROR(status)) {
+ if (send_tok.length > 0) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+ packet_put_string(send_tok.value, send_tok.length);
+ packet_send();
+ gss_release_buffer(&ms, &send_tok);
+ }
+ /* Start again with the next method in the list */
+ userauth(authctxt, NULL);
+ return;
+ }
+
+ if (send_tok.length > 0) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+ packet_put_string(send_tok.value, send_tok.length);
+ packet_send();
+ gss_release_buffer(&ms, &send_tok);
+ }
+
+ if (status == GSS_S_COMPLETE) {
+ /* If that succeeded, send a exchange complete message */
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE);
+ packet_send();
+ }
+}
+
+void
+input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc recv_tok;
+ OM_uint32 status, ms;
+
+ if (authctxt == NULL)
+ fatal("input_gssapi_response: no authentication context");
+ gssctxt = authctxt->methoddata;
+
+ recv_tok.value = packet_get_string(&recv_tok.length);
+
+ packet_check_eom();
+
+ /* Stick it into GSSAPI and see what it says */
+ status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+ &recv_tok, &send_tok, NULL);
+
+ xfree(recv_tok.value);
+ gss_release_buffer(&ms, &send_tok);
+
+ /* Server will be returning a failed packet after this one */
+}
+
+void
+input_gssapi_error(int type, u_int32_t plen, void *ctxt)
+{
+ OM_uint32 maj, min;
+ char *msg;
+ char *lang;
+
+ maj=packet_get_int();
+ min=packet_get_int();
+ msg=packet_get_string(NULL);
+ lang=packet_get_string(NULL);
+
+ packet_check_eom();
+
+ debug("Server GSSAPI Error:\n%s\n", msg);
+ xfree(msg);
+ xfree(lang);
+}
+#endif /* GSSAPI */
+
int
userauth_none(Authctxt *authctxt)
{
&input_userauth_passwd_changereq);
}
-static void
-clear_auth_state(Authctxt *authctxt)
-{
- /* XXX clear authentication state */
- dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, NULL);
-}
-
static int
identity_sign(Identity *id, u_char **sigp, u_int *lenp,
u_char *data, u_int datalen)
Buffer b;
u_char *blob, *signature;
u_int bloblen, slen;
- int skip = 0;
+ u_int skip = 0;
int ret = -1;
int have_sig = 1;
return 1;
}
-#if KRB5
-static int
-ssh_krb5_helper(krb5_data *ap)
-{
- krb5_context xcontext = NULL; /* XXX share with ssh1 */
- krb5_auth_context xauth_context = NULL;
-
- krb5_context *context;
- krb5_auth_context *auth_context;
- krb5_error_code problem;
- const char *tkfile;
- struct stat buf;
- krb5_ccache ccache = NULL;
- const char *remotehost;
- int ret;
-
- memset(ap, 0, sizeof(*ap));
-
- context = &xcontext;
- auth_context = &xauth_context;
-
- problem = krb5_init_context(context);
- if (problem) {
- debug("Kerberos v5: krb5_init_context failed");
- ret = 0;
- goto out;
- }
-
- tkfile = krb5_cc_default_name(*context);
- if (strncmp(tkfile, "FILE:", 5) == 0)
- tkfile += 5;
-
- if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) {
- debug("Kerberos v5: could not get default ccache (permission denied).");
- ret = 0;
- goto out;
- }
-
- problem = krb5_cc_default(*context, &ccache);
- if (problem) {
- debug("Kerberos v5: krb5_cc_default failed: %s",
- krb5_get_err_text(*context, problem));
- ret = 0;
- goto out;
- }
-
- remotehost = get_canonical_hostname(1);
-
- problem = krb5_mk_req(*context, auth_context, AP_OPTS_MUTUAL_REQUIRED,
- "host", remotehost, NULL, ccache, ap);
- if (problem) {
- debug("Kerberos v5: krb5_mk_req failed: %s",
- krb5_get_err_text(*context, problem));
- ret = 0;
- goto out;
- }
- ret = 1;
-
- out:
- if (ccache != NULL)
- krb5_cc_close(*context, ccache);
- if (*auth_context)
- krb5_auth_con_free(*context, *auth_context);
- if (*context)
- krb5_free_context(*context);
- return (ret);
-}
-
-int
-userauth_kerberos(Authctxt *authctxt)
-{
- krb5_data ap;
-
- if (ssh_krb5_helper(&ap) == 0)
- return (0);
-
- packet_start(SSH2_MSG_USERAUTH_REQUEST);
- packet_put_cstring(authctxt->server_user);
- packet_put_cstring(authctxt->service);
- packet_put_cstring(authctxt->method->name);
- packet_put_string(ap.data, ap.length);
- packet_send();
-
- krb5_data_free(&ap);
- return (1);
-}
-#endif
-
/* find auth method */
/*