static int gssapi_setup(struct openconnect_info *vpninfo, const char *service, int proxy) { OM_uint32 major, minor; gss_buffer_desc token = GSS_C_EMPTY_BUFFER; char *name; if (asprintf(&name, "%s@%s", service, proxy ? vpninfo->proxy : vpninfo->hostname) == -1) return -ENOMEM; token.length = strlen(name); token.value = name; major = gss_import_name(&minor, &token, (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &vpninfo->gss_target_name[proxy]); free(name); if (GSS_ERROR(major)) { vpn_progress(vpninfo, PRG_ERR, _("Error importing GSSAPI name for authentication:\n")); print_gss_err(vpninfo, "gss_import_name()", GSS_C_NO_OID, major, minor); return -EIO; } return 0; }
int gssapi_authorization(struct openconnect_info *vpninfo, int proxy, struct http_auth_state *auth_state, struct oc_text_buf *hdrbuf) { OM_uint32 major, minor; gss_buffer_desc in = GSS_C_EMPTY_BUFFER; gss_buffer_desc out = GSS_C_EMPTY_BUFFER; gss_OID mech = GSS_C_NO_OID; if (auth_state->state == AUTH_AVAILABLE && gssapi_setup(vpninfo, "HTTP", proxy)) { auth_state->state = AUTH_FAILED; return -EIO; } if (auth_state->challenge && *auth_state->challenge) { int len = -EINVAL; in.value = openconnect_base64_decode(&len, auth_state->challenge); if (!in.value) return len; in.length = len; } else if (auth_state->state > AUTH_AVAILABLE) { /* This indicates failure. We were trying, but got an empty 'Proxy-Authorization: Negotiate' header back from the server implying that we should start again... */ goto fail_gssapi; } major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &vpninfo->gss_context[proxy], vpninfo->gss_target_name[proxy], (gss_OID)&gss_mech_spnego, GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &in, &mech, &out, NULL, NULL); if (in.value) free(in.value); if (major == GSS_S_COMPLETE) auth_state->state = GSSAPI_COMPLETE; else if (major == GSS_S_CONTINUE_NEEDED) auth_state->state = GSSAPI_CONTINUE; else { vpn_progress(vpninfo, PRG_ERR, _("Error generating GSSAPI response:\n")); print_gss_err(vpninfo, "gss_init_sec_context()", mech, major, minor); fail_gssapi: auth_state->state = AUTH_FAILED; cleanup_gssapi_auth(vpninfo, proxy, auth_state); /* If we were *trying*, then -EAGAIN. Else -ENOENT to let another auth method try without having to reconnect first. */ return in.value ? -EAGAIN : -ENOENT; } buf_append(hdrbuf, "%sAuthorization: Negotiate ", proxy ? "Proxy-" : ""); buf_append_base64(hdrbuf, out.value, out.length); buf_append(hdrbuf, "\r\n"); gss_release_buffer(&minor, &out); if (!auth_state->challenge) vpn_progress(vpninfo, PRG_INFO, _("Attempting GSSAPI authentication to proxy\n")); return 0; }
int socks_gssapi_auth(struct openconnect_info *vpninfo) { gss_buffer_desc in = GSS_C_EMPTY_BUFFER; gss_buffer_desc out = GSS_C_EMPTY_BUFFER; gss_OID mech = GSS_C_NO_OID; OM_uint32 major, minor; unsigned char *pktbuf; int i; int ret = -EIO; if (gssapi_setup(vpninfo, "rcmd", 1)) return -EIO; pktbuf = malloc(65538); if (!pktbuf) return -ENOMEM; while (1) { major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &vpninfo->gss_context[1], vpninfo->gss_target_name[1], (gss_OID)&gss_mech_spnego, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_DELEG_FLAG | GSS_C_SEQUENCE_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &in, &mech, &out, NULL, NULL); in.value = NULL; if (major == GSS_S_COMPLETE) { /* If we still have a token to send, send it. */ if (!out.length) { vpn_progress(vpninfo, PRG_DEBUG, _("GSSAPI authentication completed\n")); gss_release_buffer(&minor, &out); ret = 0; break; } } else if (major != GSS_S_CONTINUE_NEEDED) { print_gss_err(vpninfo, "gss_init_sec_context()", mech, major, minor); break; } if (out.length > 65535) { vpn_progress(vpninfo, PRG_ERR, _("GSSAPI token too large (%zd bytes)\n"), out.length); break; } pktbuf[0] = 1; /* ver */ pktbuf[1] = 1; /* mtyp */ store_be16(pktbuf + 2, out.length); memcpy(pktbuf + 4, out.value, out.length); free(out.value); vpn_progress(vpninfo, PRG_TRACE, _("Sending GSSAPI token of %zu bytes\n"), out.length + 4); i = vpninfo->ssl_write(vpninfo, (void *)pktbuf, out.length + 4); if (i < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to send GSSAPI authentication token to proxy: %s\n"), strerror(-i)); break; } i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, 4); if (i < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to receive GSSAPI authentication token from proxy: %s\n"), strerror(-i)); break; } if (pktbuf[1] == 0xff) { vpn_progress(vpninfo, PRG_ERR, _("SOCKS server reported GSSAPI context failure\n")); break; } else if (pktbuf[1] != 1) { vpn_progress(vpninfo, PRG_ERR, _("Unknown GSSAPI status response (0x%02x) from SOCKS server\n"), pktbuf[1]); break; } in.length = load_be16(pktbuf + 2); in.value = pktbuf; if (!in.length) { vpn_progress(vpninfo, PRG_DEBUG, _("GSSAPI authentication completed\n")); ret = 0; break; } i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, in.length); if (i < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to receive GSSAPI authentication token from proxy: %s\n"), strerror(-i)); break; } vpn_progress(vpninfo, PRG_TRACE, _("Got GSSAPI token of %zu bytes: %02x %02x %02x %02x\n"), in.length, pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3]); } if (!ret) { ret = -EIO; pktbuf[0] = 0; in.value = pktbuf; in.length = 1; major = gss_wrap(&minor, vpninfo->gss_context[1], 0, GSS_C_QOP_DEFAULT, &in, NULL, &out); if (major != GSS_S_COMPLETE) { print_gss_err(vpninfo, "gss_wrap()", mech, major, minor); goto err; } pktbuf[0] = 1; pktbuf[1] = 2; store_be16(pktbuf + 2, out.length); memcpy(pktbuf + 4, out.value, out.length); free(out.value); vpn_progress(vpninfo, PRG_TRACE, _("Sending GSSAPI protection negotiation of %zu bytes\n"), out.length + 4); i = vpninfo->ssl_write(vpninfo, (void *)pktbuf, out.length + 4); if (i < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to send GSSAPI protection response to proxy: %s\n"), strerror(-i)); goto err; } i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, 4); if (i < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to receive GSSAPI protection response from proxy: %s\n"), strerror(-i)); goto err; } in.length = load_be16(pktbuf + 2); in.value = pktbuf; i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, in.length); if (i < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to receive GSSAPI protection response from proxy: %s\n"), strerror(-i)); goto err; } vpn_progress(vpninfo, PRG_TRACE, _("Got GSSAPI protection response of %zu bytes: %02x %02x %02x %02x\n"), in.length, pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3]); major = gss_unwrap(&minor, vpninfo->gss_context[1], &in, &out, NULL, GSS_C_QOP_DEFAULT); if (major != GSS_S_COMPLETE) { print_gss_err(vpninfo, "gss_unwrap()", mech, major, minor); goto err; } if (out.length != 1) { vpn_progress(vpninfo, PRG_ERR, _("Invalid GSSAPI protection response from proxy (%zu bytes)\n"), out.length); gss_release_buffer(&minor, &out); goto err; } i = *(char *)out.value; gss_release_buffer(&minor, &out); if (i == 1) { vpn_progress(vpninfo, PRG_ERR, _("SOCKS proxy demands message integrity, which is not supported\n")); goto err; } else if (i == 2) { vpn_progress(vpninfo, PRG_ERR, _("SOCKS proxy demands message confidentiality, which is not supported\n")); goto err; } else if (i) { vpn_progress(vpninfo, PRG_ERR, _("SOCKS proxy demands protection unknown type 0x%02x\n"), (unsigned char)i); goto err; } ret = 0; } err: cleanup_gssapi_auth(vpninfo, 1, NULL); free(pktbuf); return ret; }
static void print_gss_errs(OM_uint32 major, OM_uint32 minor, gss_OID mech) { print_gss_err(major, GSS_C_GSS_CODE, GSS_C_NO_OID); print_gss_err(major, GSS_C_MECH_CODE, mech); }