📄 pluto_crypt.c
字号:
/* * Cryptographic helper function. * Copyright (C) 2004 Michael C. Richardson <mcr@xelerance.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * This code was developed with the support of IXIA communications. * * RCSID $Id: pluto_crypt.c,v 1.12 2004/11/30 02:27:50 mcr Exp $ */#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <sys/queue.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/types.h>#include <signal.h>#include <openswan.h>#include <openswan/ipsec_policy.h>#include "constants.h"#include "defs.h"#include "packet.h"#include "demux.h"#include "oswlog.h"#include "log.h"#include "state.h"#include "demux.h"#include "rnd.h"#include "pluto_crypt.h"struct pluto_crypto_worker { int pcw_helpernum; pid_t pcw_pid; int pcw_pipe; int pcw_work; /* how many items outstanding */ int pcw_maxbasicwork; /* how many basic things can be queued */ int pcw_maxcritwork; /* how many critical things can be queued */ bool pcw_dead; /* worker is dead, waiting for reap */ bool pcw_reaped; /* worker has been reaped, waiting for dealloc */ struct pluto_crypto_req_cont *pcw_cont;};static struct pluto_crypto_req_cont *backlogqueue;static int backlogqueue_len;static void init_crypto_helper(struct pluto_crypto_worker *w, int n);static void cleanup_crypto_helper(struct pluto_crypto_worker *w);static void handle_helper_comm(struct pluto_crypto_worker *w);extern void free_preshared_secrets(void);/* may be NULL if we are to do all the work ourselves */struct pluto_crypto_worker *pc_workers = NULL;int pc_workers_cnt = 0;int pc_worker_num;pcr_req_id pcw_id;/* local in child */int pc_helper_num=-1;void pluto_do_crypto_op(struct pluto_crypto_req *r){ DBG(DBG_CONTROL , DBG_log("helper %d doing %s op id: %u" , pc_helper_num , enum_show(&pluto_cryptoop_names, r->pcr_type) , r->pcr_id)); /* now we have the entire request in the buffer, process it */ switch(r->pcr_type) { case pcr_build_kenonce: calc_ke(r); calc_nonce(r); break; case pcr_build_nonce: calc_nonce(r); break; case pcr_rsa_sign: case pcr_rsa_check: case pcr_x509cert_fetch: case pcr_x509crl_fetch: break; }}void pluto_crypto_allocchunk(wire_chunk_t *space , wire_chunk_t *new , size_t howbig){ /* * passert for now, since we should be able to figure out what * the maximum is. */ passert(space->start + howbig < space->len); new->start = space->start; new->len = howbig; space->start += howbig;}static void catchhup(int signo UNUSED){ /* socket closed die */ exit(0);}static void catchusr1(int signo UNUSED){ return;}static voidhelper_passert_fail(const char *pred_str , const char *file_str , unsigned long line_no) NEVER_RETURNS;static voidhelper_passert_fail(const char *pred_str , const char *file_str , unsigned long line_no){ static bool dying_breath = 0; /* we will get a possibly unplanned prefix. Hope it works */ loglog(RC_LOG_SERIOUS, "ASSERTION FAILED at %s:%lu: %s", file_str, line_no, pred_str); if (!dying_breath) { dying_breath = TRUE; } chdir("helper"); abort();}void pluto_crypto_helper(int fd, int helpernum){ char reqbuf[PCR_REQ_SIZE]; struct pluto_crypto_req *r; signal(SIGHUP, catchhup); signal(SIGUSR1, catchusr1); pc_worker_num = helpernum; /* make us lower priority that average */ setpriority(PRIO_PROCESS, 0, 10); DBG(DBG_CONTROL, DBG_log("helper %d waiting on fd: %d" , helpernum, fd)); while(read(fd, reqbuf, sizeof(r->pcr_len)) == sizeof(r->pcr_len)) { int restlen; int actlen; r = (struct pluto_crypto_req *)reqbuf; restlen = r->pcr_len-sizeof(r->pcr_len); passert(restlen < (signed)PCR_REQ_SIZE); /* okay, got a basic size, read the rest of it */ if((actlen= read(fd, reqbuf+sizeof(r->pcr_len), restlen)) != restlen) { /* faulty read. die, parent will restart us */ loglog(RC_LOG_SERIOUS, "cryptographic helper(%d) read(%d)=%d failed: %s\n", getpid(), restlen, actlen, strerror(errno)); exit(1); } pluto_do_crypto_op(r); actlen = write(fd, (unsigned char *)r, r->pcr_len); if((unsigned)actlen != r->pcr_len) { loglog(RC_LOG_SERIOUS, "failed to write answer: %d", actlen); exit(2); } } /* probably normal EOF */ close(fd); exit(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){ struct pluto_crypto_worker *w; int cnt; /* do it all ourselves? */ if(pc_workers == NULL) { reset_cur_state(); pluto_do_crypto_op(r); /* call the continuation */ (*cn->pcrc_func)(cn, r, NULL); pfree(cn); pfree(r); return NULL; } /* set up the id */ r->pcr_id = pcw_id++; cn->pcrc_id = r->pcr_id; cn->pcrc_pcr = r; pc_worker_num++; if(pc_worker_num >= pc_workers_cnt) { pc_worker_num = 0; } cnt = pc_workers_cnt; /* find an available worker, restarting one if it was found to be dead */ 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)); while((w->pcw_dead || (w->pcw_work >= w->pcw_maxbasicwork)) && --cnt > 0) { pc_worker_num++; 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); } 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)); } if(cnt == 0 && r->pcr_pcim > pcim_ongoing_crypto) { cnt = pc_workers_cnt; while((w->pcw_dead || (w->pcw_work >= w->pcw_maxcritwork)) && --cnt > 0) { pc_worker_num++; 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); } 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 */ cn->pcrc_next = backlogqueue; backlogqueue = cn; backlogqueue_len++; DBG(DBG_CONTROL , DBG_log("critical demand crypto operation queued as item %d" , backlogqueue_len)); return NULL; } if(cnt == 0) { /* didn't find any workers */ DBG(DBG_CONTROL , DBG_log("failed to find any available worker")); 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")); return "failed to start a new helper"; } } /* link it to the active worker list */ cn->pcrc_next = w->pcw_cont; w->pcw_cont = cn; passert(w->pcw_pid != -1); passert(w->pcw_pipe != -1); passert(w->pcw_work < w->pcw_maxcritwork); DBG(DBG_CONTROL , DBG_log("asking helper %d to do %s op on seq: %u" , w->pcw_helpernum , enum_show(&pluto_cryptoop_names, r->pcr_type) , r->pcr_id)); /* send the request, and then mark the work as having more work */ cnt = write(w->pcw_pipe, r, r->pcr_len); if(cnt == -1) { return "failed to write"; } w->pcw_work++; return NULL;}/* * send 1 unit of backlog, if any, to indicated worker. */static void crypto_send_backlog(struct pluto_crypto_worker *w){ struct pluto_crypto_req *r; struct pluto_crypto_req_cont *cn; if(backlogqueue_len > 0) { int cnt; passert(backlogqueue != NULL); cn = backlogqueue; backlogqueue = cn->pcrc_next; backlogqueue_len--; r = cn->pcrc_pcr; DBG(DBG_CONTROL , DBG_log("removing backlog item (%d) from queue: %d left" , r->pcr_id, backlogqueue_len)); /* 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")); /* XXX invoke callback with failure */ passert(0); return; } } /* link it to the active worker list */ cn->pcrc_next = w->pcw_cont; w->pcw_cont = cn;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -