Beispiel #1
0
stf_status send_crypto_helper_request(struct pluto_crypto_req *r,
				 struct pluto_crypto_req_cont *cn)
{
	static int pc_worker_num = 0;	/* index of last worker assigned work */
	struct pluto_crypto_worker *w;	/* best worker for task */
	struct pluto_crypto_worker *c;	/* candidate worker */
	struct state *st = cur_state;	/* TRANSITIONAL */

	/*
	 * transitional: caller must have set pcrc_serialno.
	 * It ought to match cur_state->st_serialno.
	 */
	passert(cn->pcrc_serialno == st->st_serialno);

	passert(st->st_serialno != SOS_NOBODY);
	cn->pcrc_serialno = st->st_serialno;

	passert(cn->pcrc_func != NULL);

	/* do it all ourselves? */
	if (pc_workers == NULL) {
		reset_cur_state();

		pluto_do_crypto_op(r, -1);

		/* call the continuation */
		(*cn->pcrc_func)(cn, r);

		pfree(cn);	/* ownership transferred from caller */

		/* indicate that we completed the work */
		return STF_INLINE;
	}

	/* attempt to send to a worker thread */

	/* set up the id */
	r->pcr_id = pcw_id++;
	cn->pcrc_id = r->pcr_id;
	cn->pcrc_pcr = r;

	/* Find the first of the least-busy workers (if any) */

	w = NULL;
	for (c = pc_workers; c != &pc_workers[pc_workers_cnt]; c++) {
		DBG(DBG_CONTROL,
		    DBG_log("crypto helper %d%s: pcw_work: %d",
			    pc_worker_num,
			    c->pcw_dead? " DEAD" : "",
			    c->pcw_work));

		if (!c->pcw_dead && (w == NULL || c->pcw_work < w->pcw_work)) {
			w = c;	/* c is the best so far */
			if (c->pcw_work == 0)
				break;	/* early out: cannot do better */
		}
	}

	if (w != NULL &&
	    (w->pcw_work < w->pcw_maxbasicwork ||
	      (w->pcw_work < w->pcw_maxcritwork && r->pcr_pcim > pcim_ongoing_crypto)))
	{
		/* allocate task to worker w */

		/* link it to the worker's active list
		 * cn transferred from caller
		 */
		TAILQ_INSERT_TAIL(&w->pcw_active, cn, pcrc_list);

		passert(w->pcw_master_fd != -1);

		cn->pcrc_reply_stream = reply_stream;
		if (pbs_offset(&reply_stream) != 0) {
			/* copy partially built reply stream to heap
			 * IMPORTANT: don't leak this.
			 */
			cn->pcrc_reply_buffer =
				clone_bytes(reply_stream.start,
					    pbs_offset(&reply_stream),
						       "saved reply buffer");
		}

		if (!crypto_write_request(w, r)) {
			/* free the heap space */
			if (pbs_offset(&cn->pcrc_reply_stream) != 0)
				pfree(cn->pcrc_reply_buffer);
			cn->pcrc_reply_buffer = NULL;
			loglog(RC_LOG_SERIOUS, "cannot start crypto helper %d: failed to write",
				w->pcw_helpernum);
			return STF_FAIL;
		}

		w->pcw_work++;
	} else if (r->pcr_pcim >= pcim_demand_crypto) {
		/* Task is important: put it all on the backlog queue for later */

		/* cn transferred from caller */
		TAILQ_INSERT_TAIL(&backlog, cn, pcrc_list);

		/* copy the request */
		r = clone_bytes(r, r->pcr_len, "saved crypto request");
		cn->pcrc_pcr = r;

		cn->pcrc_reply_stream = reply_stream;
		if (pbs_offset(&reply_stream) != 0) {
			/* copy partially built reply stream to heap
			 * IMPORTANT: don't leak this.
			 */
			cn->pcrc_reply_buffer =
				clone_bytes(reply_stream.start,
					    pbs_offset(&reply_stream),
					    "saved reply buffer");
		}

		backlog_queue_len++;
		DBG(DBG_CONTROL,
		    DBG_log("critical demand crypto operation queued on backlog as %dth item; request ID %u",
			    backlog_queue_len, r->pcr_id));
	} else {
		/* didn't find any available workers */
		DBG(DBG_CONTROL,
		    DBG_log("failed to find any available crypto worker (import=%s)",
			    enum_name(&pluto_cryptoimportance_names,
				      r->pcr_pcim)));

		loglog(RC_LOG_SERIOUS, "cannot start crypto helper: failed to find any available worker");

		pfree(cn);	/* ownership transferred from caller */
		return STF_TOOMUCHCRYPTO;
	}

	/* cn ownership transferred on to backlog */

	DBG(DBG_CONTROLMORE, DBG_log("#%lu %s:%u st->st_calculating = TRUE;", st->st_serialno, __FUNCTION__, __LINE__));
	st->st_calculating = TRUE;
	delete_event(st);
	event_schedule(EVENT_CRYPTO_FAILED, EVENT_CRYPTO_FAILED_DELAY, st);

	return STF_SUSPEND;
}
Beispiel #2
0
/*
 * for aggressive mode, this is sub-optimal, since we should have
 * had the crypto helper actually do everything, but we need to do
 * some additional work to set that all up, so this is fine for now.
 *
 */
static void aggr_inI1_outR1_continue1(struct pluto_crypto_req_cont *pcrc,
				      struct pluto_crypto_req *r,
				      err_t ugh)
{
	struct ke_continuation *ke = (struct ke_continuation *)pcrc;
	struct msg_digest *md = ke->ke_md;
	struct state *const st = md->st;
	stf_status e;

	DBG(DBG_CONTROLMORE,
	    DBG_log("aggr inI1_outR1: calculated ke+nonce, calculating DH"));

	if (st == NULL) {
		loglog(RC_LOG_SERIOUS,
		       "%s: Request was disconnected from state",
		       __FUNCTION__);
		release_any_md(&ke->ke_md);
		return;
	}

	/* XXX should check out ugh */
	passert(ugh == NULL);
	passert(cur_state == NULL);
	passert(st != NULL);

	passert(st->st_suspended_md == ke->ke_md);
	unset_suspended(st); /* no longer connected or suspended */

	set_cur_state(st);
	DBG(DBG_CONTROLMORE, DBG_log("#%lu %s:%u st->st_calculating = FALSE;", st->st_serialno, __FUNCTION__, __LINE__));
	st->st_calculating = FALSE;

	/* unpack first calculation */
	unpack_KE(st, r, &st->st_gr);

	/* unpack nonce too */
	unpack_nonce(&st->st_nr, r);

	/* NOTE: the "r" reply will get freed by our caller */

	/* set up second calculation */
	{
		struct dh_continuation *dh = alloc_thing(
			struct dh_continuation,
			"aggr outR1 DH");

		dh->dh_md = md;
		set_suspended(st, md);
		dh->dh_pcrc.pcrc_serialno = st->st_serialno;	/* transitional */
		pcrc_init(&dh->dh_pcrc, aggr_inI1_outR1_continue2);
		e = start_dh_secretiv(&dh->dh_pcrc, st,
				      st->st_import,
				      O_RESPONDER,
				      st->st_oakley.group->group);

		if (e != STF_SUSPEND) {
			if (dh->dh_md != NULL) {
				complete_v1_state_transition(&dh->dh_md, e);
				release_any_md(&dh->dh_md);
			}
		}

		reset_cur_state();
	}
}
Beispiel #3
0
/*
 * this function is called with a request to do some cryptographic operations
 * along with a continuation structure, which will be used to deal with
 * the response.
 *
 * This may fail if there are no helpers that can take any data, in which
 * case an error is returned.
 *
 */
err_t send_crypto_helper_request(struct pluto_crypto_req *r
				 , struct pluto_crypto_req_cont *cn
				 , bool *toomuch)
{
    struct pluto_crypto_worker *w;
    int cnt;

    /* do it all ourselves? */
    if(pc_workers == NULL) {
	reset_cur_state();

#ifdef HAVE_LIBNSS
	pluto_do_crypto_op(r,pc_helper_num);
#else
	pluto_do_crypto_op(r);
#endif
	/* call the continuation */
	(*cn->pcrc_func)(cn, r, NULL);

	/* indicate that we did everything ourselves */
	*toomuch = TRUE;

	pfree(cn);
	return NULL;
    }

    /* set up the id */
    r->pcr_id = pcw_id++;
    cn->pcrc_id = r->pcr_id;
    cn->pcrc_pcr = r;

    /* find an available worker */
    cnt = pc_workers_cnt;
    do {
	pc_worker_num++;
 	if(pc_worker_num >= pc_workers_cnt) {
 	    pc_worker_num = 0;
 	}
	w = &pc_workers[pc_worker_num];

 	DBG(DBG_CONTROL
 	    , DBG_log("%d: w->pcw_dead: %d w->pcw_work: %d cnt: %d",
 		      pc_worker_num, w->pcw_dead, w->pcw_work, cnt));

	/* see if there is something to clean up after */
	if(w->pcw_dead      == TRUE
	   && w->pcw_reaped == TRUE) {
	    cleanup_crypto_helper(w, 0);
 	    DBG(DBG_CONTROL
 		, DBG_log("clnup %d: w->pcw_dead: %d w->pcw_work: %d cnt: %d",
 			  pc_worker_num, w->pcw_dead, w->pcw_work, cnt));
	}
    } while(((w->pcw_work >= w->pcw_maxbasicwork))
 	    && --cnt > 0);

    if(cnt == 0 && r->pcr_pcim > pcim_ongoing_crypto) {
	cnt = pc_workers_cnt;
 	while((w->pcw_work >= w->pcw_maxcritwork)
	      && --cnt > 0) {

 	    /* find an available worker */
	    pc_worker_num++;
 	    if(pc_worker_num >= pc_workers_cnt) {
 		pc_worker_num = 0;
  	    }

	    w = &pc_workers[pc_worker_num];
	    /* see if there is something to clean up after */
	    if(w->pcw_dead      == TRUE
	       && w->pcw_reaped == TRUE) {
		cleanup_crypto_helper(w, 0);
	    }
	}
	DBG(DBG_CONTROL
	    , DBG_log("crit %d: w->pcw_dead: %d w->pcw_work: %d cnt: %d",
		      pc_worker_num, w->pcw_dead, w->pcw_work, cnt));
    }

    if(cnt == 0 && r->pcr_pcim >= pcim_demand_crypto) {
	/* it is very important. Put it all on a queue for later */

	TAILQ_INSERT_TAIL(&backlog, cn, pcrc_list);

	/* copy the request */
	r = clone_bytes(r, r->pcr_len, "saved cryptorequest");
	cn->pcrc_pcr = r;

	cn->pcrc_reply_stream = reply_stream;
	if (pbs_offset(&reply_stream)) {
	    cn->pcrc_reply_buffer = clone_bytes(reply_stream.start
		    , pbs_offset(&reply_stream), "saved reply buffer");
	}

	backlogqueue_len++;
	DBG(DBG_CONTROL
	    , DBG_log("critical demand crypto operation queued on backlog as %d'th item, id: q#%u"
		      , backlogqueue_len, r->pcr_id));
	*toomuch = FALSE;
	return NULL;
    }

    if(cnt == 0) {
	/* didn't find any workers */
	DBG(DBG_CONTROL
	    , DBG_log("failed to find any available worker (import=%s)"
		      , enum_name(&pluto_cryptoimportance_names,r->pcr_pcim)));

	*toomuch = TRUE;
	return "failed to find any available worker";
    }

    /* w points to a work. Make sure it is live */
    if(w->pcw_pid == -1) {
	init_crypto_helper(w, pc_worker_num);
	if(w->pcw_pid == -1) {
	    DBG(DBG_CONTROL
		, DBG_log("found only a dead helper, and failed to restart it"));
	    *toomuch = TRUE;
	    return "failed to start a new helper";
	}
    }

    /* link it to the active worker list */
    TAILQ_INSERT_TAIL(&w->pcw_active, cn, pcrc_list);

    passert(w->pcw_pid != -1);
    passert(w->pcw_pipe != -1);
    passert(w->pcw_work < w->pcw_maxcritwork);

    cn->pcrc_reply_stream = reply_stream;
    if (pbs_offset(&reply_stream))
	cn->pcrc_reply_buffer = clone_bytes(reply_stream.start
		, pbs_offset(&reply_stream), "saved reply buffer");

    if(!crypto_write_request(w, r)) {
	openswan_log("failed to write crypto request: %s\n",
		     strerror(errno));
	if (pbs_offset(&cn->pcrc_reply_stream))
	    pfree(cn->pcrc_reply_buffer);
	cn->pcrc_reply_buffer = NULL;
	return "failed to write";
    }

    w->pcw_work++;
    *toomuch = FALSE;
    return NULL;
}
Beispiel #4
0
/*
 * this function is called when there is a helper pipe that is ready.
 * we read the request from the pipe, and find the associated continuation,
 * and dispatch to that continuation.
 *
 * this function should process only a single answer, and then go back
 * to the select call to get called again. This is not most efficient,
 * but is is most fair.
 *
 */
void handle_helper_comm(struct pluto_crypto_worker *w)
{
    struct pluto_crypto_req reqbuf[2];
    unsigned char *inloc;
    struct pluto_crypto_req *r;
    int restlen;
    int actlen;
    struct pluto_crypto_req_cont *cn;

    DBG(DBG_CRYPT|DBG_CONTROL
	, DBG_log("helper %u has finished work (cnt now %d)"
		  ,w->pcw_helpernum
		  ,w->pcw_work));

    /* read from the pipe */
    memset(reqbuf, 0, sizeof(reqbuf));
    actlen = read(w->pcw_pipe, (char *)reqbuf, sizeof(r->pcr_len));

    if(actlen != sizeof(r->pcr_len)) {
	if(actlen != 0) {
	    loglog(RC_LOG_SERIOUS, "read failed with %d: %s"
		   , actlen, strerror(errno));
	}
	/*
	 * eof, mark worker as dead. If already reaped, then free.
	 */
	w->pcw_dead = TRUE;
	if(w->pcw_reaped) {
	    cleanup_crypto_helper(w, 0);
	}
	return;
    }

    /* we can accept more work now that we have read from the pipe */
    w->pcw_work--;

    r = &reqbuf[0];

    if(r->pcr_len > sizeof(reqbuf)) {
	loglog(RC_LOG_SERIOUS, "helper(%d) pid=%lu screwed up length: %lu > %lu, killing it"
	       , w->pcw_helpernum
	       , (unsigned long)w->pcw_pid, (unsigned long)r->pcr_len
               , (unsigned long)sizeof(reqbuf));
    killit:
#ifdef HAVE_LIBNSS
	pthread_cancel((pthread_t)w->pcw_pid);
#else
	kill(w->pcw_pid, SIGTERM);
#endif
	w->pcw_dead = TRUE;
	return;
    }

    restlen = r->pcr_len-sizeof(r->pcr_len);
    inloc = ((unsigned char*)reqbuf)+sizeof(r->pcr_len);

    while(restlen > 0) {
	/* okay, got a basic size, read the rest of it */
	actlen = read(w->pcw_pipe, inloc, restlen);

	if(actlen <= 0) {
	    /* faulty read. note this fact, and close pipe. */
	    /* we actually need to restart this query, but we'll do that
	     * another day.
	     */
	    loglog(RC_LOG_SERIOUS
		   , "cryptographic handler(%d) read(%d)=%d failed: %s\n"
		   , w->pcw_pipe, restlen, actlen, strerror(errno));
	    goto killit;
	}

	restlen -= actlen;
	inloc   += actlen;
    }

    DBG(DBG_CRYPT|DBG_CONTROL, DBG_log("helper %u replies to id: q#%u"
				       ,w->pcw_helpernum
				       ,r->pcr_id));

    /*
     * if there is work queued, then send it off after reading, since this
     * avoids the most deadlocks
     */
    crypto_send_backlog(w);

    /* now match up request to continuation, and invoke it */
    for(cn = w->pcw_active.tqh_first;
	cn!=NULL && r->pcr_id != cn->pcrc_id;
	cn = cn->pcrc_list.tqe_next);

    if(cn == NULL) {
	loglog(RC_LOG_SERIOUS
	       , "failed to find continuation associated with req %u\n",
	       (unsigned int)r->pcr_id);
	return;
    }

    /* unlink it */
    TAILQ_REMOVE(&w->pcw_active, cn, pcrc_list);

    passert(cn->pcrc_func != NULL);

    DBG(DBG_CRYPT, DBG_log("calling callback function %p"
			   ,cn->pcrc_func));

    reply_stream = cn->pcrc_reply_stream;
    if (pbs_offset(&reply_stream)) {
	memcpy(reply_stream.start, cn->pcrc_reply_buffer
		, pbs_offset(&reply_stream));
	pfree(cn->pcrc_reply_buffer);
    }
    cn->pcrc_reply_buffer = NULL;

    /* call the continuation */
    cn->pcrc_pcr = r;
    reset_cur_state();
    (*cn->pcrc_func)(cn, r, NULL);

    /* now free up the continuation */
    pfree(cn);
}
Beispiel #5
0
/*
 * This function is called when there is socketpair input from a helper.
 * This is the answer from the helper.
 * We read the request from the socketpair, and find the associated continuation,
 * and dispatch to that continuation.
 *
 * This function should process only a single answer, and then go back
 * to the select call to get called again. This is not most efficient,
 * but is is most fair.
 *
 */
static void handle_helper_answer(struct pluto_crypto_worker *w)
{
	struct pluto_crypto_req rr;
	ssize_t actlen;
	struct pluto_crypto_req_cont *cn;

	DBG(DBG_CONTROL,
	    DBG_log("crypto helper %d has finished work (pcw_work now %d)",
		    w->pcw_helpernum,
		    w->pcw_work));

	/* read from the socketpair in one gulp */

	errno = 0;
	actlen = read(w->pcw_master_fd, (void *)&rr, sizeof(rr));

	if (actlen != sizeof(rr)) {
		if (actlen == -1) {
			loglog(RC_LOG_SERIOUS,
			       "read from crypto helper %d failed: %s.  Killing helper.",
			       w->pcw_helpernum, strerror(errno));
			kill_helper(w);
		} else if (actlen == 0) {
			/* EOF: mark worker as dead. */
			w->pcw_dead = TRUE;
		} else if (errno == 0) {
			loglog(RC_LOG_SERIOUS,
			       "read from crypto helper %d failed with short length %zd of %zu.  Killing helper.",
			       w->pcw_helpernum, actlen, sizeof(rr));
			kill_helper(w);
		} else {
			loglog(RC_LOG_SERIOUS,
			       "read from crypto helper %d failed with short length %zd of %zu (errno=%s).  Killing helper.",
			       w->pcw_helpernum, actlen, sizeof(rr), strerror(errno));
			kill_helper(w);
		}
		return;
	}

	if (rr.pcr_len != sizeof(rr)) {
		loglog(RC_LOG_SERIOUS,
		       "crypto helper %d screwed up length: %zu != %zu; killing it",
		       w->pcw_helpernum,
		       rr.pcr_len, sizeof(rr));
		kill_helper(w);
		return;
	}

	DBG(DBG_CONTROL,
		DBG_log("crypto helper %d replies to request ID %u",
			w->pcw_helpernum, rr.pcr_id));

	/* worker w can accept more work now that we have read from its socketpair */
	w->pcw_work--;

	/*
	 * if there is work queued, then send it off after reading, since this
	 * avoids the most deadlocks
	 */
	crypto_send_backlog(w);

	/* now match up request to continuation, and invoke it */
	for (cn = w->pcw_active.tqh_first;
	     cn != NULL && rr.pcr_id != cn->pcrc_id;
	     cn = cn->pcrc_list.tqe_next)
		;

	if (cn == NULL) {
		loglog(RC_LOG_SERIOUS,
		       "failed to find crypto continuation associated with request ID %u performed by crypto helper %d",
		       rr.pcr_id,
		       w->pcw_helpernum);
		return;
	}

	/* unlink it */
	TAILQ_REMOVE(&w->pcw_active, cn, pcrc_list);

	passert(cn->pcrc_func != NULL);

	DBG(DBG_CONTROL,
		DBG_log("calling continuation function %p",
			cn->pcrc_func));

	reply_stream = cn->pcrc_reply_stream;
	if (pbs_offset(&reply_stream) != 0) {
		memcpy(reply_stream.start, cn->pcrc_reply_buffer,
		       pbs_offset(&reply_stream));
		pfree(cn->pcrc_reply_buffer);
	}
	cn->pcrc_reply_buffer = NULL;

	/* call the continuation (skip if suppressed) */
	cn->pcrc_pcr = &rr;
	reset_cur_state();
	if (cn->pcrc_serialno != SOS_NOBODY)
		(*cn->pcrc_func)(cn, &rr);

	/* now free up the continuation */
	pfree(cn);
}