+/* This is used by incomplete GSSAPI implementations that can't use
+ * gss_init_sec_context and will parse the token themselves (gssnull and sk).
+ * Callers should have cred->lc_mech_token pointing to a gss_buffer_desc
+ * token to send to the peer as part of the SEC_CTX_INIT operation. The return
+ * RPC's token with be in gr.gr_token which is validated using
+ * lgss_validate_cred. */
+static int lgssc_negotiation_manual(struct lgss_nego_data *lnd,
+ struct lgss_cred *cred)
+{
+ struct lgss_init_res gr;
+ OM_uint32 min_stat;
+ int rc;
+
+ logmsg(LL_TRACE, "starting gss negotation\n");
+ memset(&gr, 0, sizeof(gr));
+
+ lnd->lnd_rpc_err = do_nego_rpc(lnd, &cred->lc_mech_token, &gr);
+ if (lnd->lnd_rpc_err) {
+ logmsg(LL_ERR, "negotiation rpc error %d\n", lnd->lnd_rpc_err);
+ rc = lnd->lnd_rpc_err;
+ goto out_error;
+ }
+
+ if (gr.gr_major == GSS_S_CONTINUE_NEEDED) {
+ rc = -EAGAIN;
+ goto out_error;
+
+ } else if (gr.gr_major != GSS_S_COMPLETE) {
+ lnd->lnd_gss_err = gr.gr_major;
+ logmsg(LL_ERR, "negotiation gss error %x\n", lnd->lnd_gss_err);
+ rc = -ENOTCONN;
+ goto out_error;
+ }
+
+ if (gr.gr_ctx.length == 0 || gr.gr_token.length == 0) {
+ logmsg(LL_ERR, "zero length context or token received\n");
+ rc = -EINVAL;
+ goto out_error;
+ }
+
+ rc = lgss_validate_cred(cred, &gr.gr_token, &lnd->lnd_ctx_token);
+ if (rc) {
+ logmsg(LL_ERR, "peer token failed validation\n");
+ goto out_error;
+ }
+
+ lnd->lnd_established = 1;
+ lnd->lnd_seq_win = gr.gr_win;
+ lnd->lnd_rmt_ctx = gr.gr_ctx;
+
+ if (gr.gr_token.length != 0)
+ gss_release_buffer(&min_stat, &gr.gr_token);
+
+ logmsg(LL_DEBUG, "successfully negotiated a context\n");
+ return 0;
+
+out_error:
+ if (gr.gr_ctx.length != 0)
+ gss_release_buffer(&min_stat, &gr.gr_ctx);
+ if (gr.gr_token.length != 0)
+ gss_release_buffer(&min_stat, &gr.gr_token);
+
+ return rc;
+}
+