diff --git a/modules/exec/README b/modules/exec/README index 48f73ae22d9..3fe8ed72a19 100644 --- a/modules/exec/README +++ b/modules/exec/README @@ -35,6 +35,8 @@ Jan Janak 1.4.2. exec_msg(command) 1.4.3. exec_avp(command[, avplist]) 1.4.4. exec_getenv(environment_variable[, avp]) + 1.4.5. exec(command, [output], [input], + [error],[envavp]) 1.5. Known Issues @@ -47,6 +49,7 @@ Jan Janak 1.5. exec_msg usage 1.6. exec_avp usage 1.7. exec_getenv usage + 1.8. exec usage Chapter 1. Admin Guide @@ -120,9 +123,10 @@ modparam("exec", "time_to_kill", 20) 1.3.3. async (integer) - Turns on the asynchronous mode for the 'exec_msg' function. All - commands will be executed by a different process and the caller - will continue its flow, without waiting for a response. + Turns on the asynchronous mode for the 'exec_msg' and 'exec' + functions. All commands will be executed by a different process + and the caller will continue its flow, without waiting for a + response. Default value is 0 (disabled). @@ -243,6 +247,54 @@ exec_getenv("HOSTNAME"); exec_getenv("HOSTNAME", "$avp(localhost)"); ... +1.4.5. exec(command, [output], [input], [error],[envavp]) + + Executes an external command. The input is passed to the + standard input of the new process, if specified, and the output + is saved in the output variable. + + Meaning of the parameters is as follows: + * command - command to be executed.It can include + pseudovariables. + * output - pseudovariable where to store the output from the + standard output of the process. Keep in mind that if this + parameter is set, the async paramater will not be taken in + consideration. + * input - String to be passed to the standard input of the + command. The string can be given as a pseudovariable. + * error - pseudovariable where to store the error from the + standard error of the process. + * envavp - Avp where to store the values for the environment + variables to be passed for the command. The names of the + environment variables will be "OSIPS_EXEC_#" where # will + start from 0. For example if you store 2 values into an avp + ("a" and "b") OSIPS_EXEC_0 will contain the first value and + OSIPS_EXEC_1 the second value. + + WARNING: any OpenSIPS pseudo-vars which may contain special + bash characters should be placed inside quotes, e.g. + exec("update-stats.sh '$ct'"); + + WARNING: "input"/"output"/"error" parameters are not designed + for a large amount of data so one should be careful when using + them because server could considerably be slowed down. + + This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, + LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, + ONREPLY_ROUTE. + + Example 1.8. exec usage +... +$avp(env) = "a"; +$avp(env) = "b"; +exec("ls -l", "$var(out)",, "$avp(env)"); +xlog("The output is $var(out)\n"); +... +$var(input) = "input"; +exec("/home/../myscript.sh",, "this is my $var(input) for exec\n", "$avp +(env)"); +... + 1.5. Known Issues When imposing an execution timeout using time_to_kill, make diff --git a/modules/exec/doc/exec_admin.xml b/modules/exec/doc/exec_admin.xml index f35287e1a5a..3e53a7291fb 100644 --- a/modules/exec/doc/exec_admin.xml +++ b/modules/exec/doc/exec_admin.xml @@ -1,9 +1,9 @@ - + &adminguide; - +
Overview @@ -16,23 +16,23 @@ - SIP_HF_<hf_name> contains value of each header field in - request. If a header field occurred multiple times, values are - concatenated and comma-separated. <hf_name> is in capital - letters. Ff a header-field name occurred in compact form, + SIP_HF_<hf_name> contains value of each header field in + request. If a header field occurred multiple times, values are + concatenated and comma-separated. <hf_name> is in capital + letters. Ff a header-field name occurred in compact form, <hf_name> is canonical. - SIP_TID is transaction identifier. All request retransmissions or - CANCELs/ACKs associated with a previous INVITE result in the same + SIP_TID is transaction identifier. All request retransmissions or + CANCELs/ACKs associated with a previous INVITE result in the same value. - SIP_DID is dialog identifier, which is the same as to-tag. + SIP_DID is dialog identifier, which is the same as to-tag. Initially, it is empty. @@ -48,7 +48,7 @@ - SIP_RURI is current request &uri; (if + SIP_RURI is current request &uri; (if unchanged, equal to original). @@ -148,9 +148,9 @@ modparam("exec", "time_to_kill", 20)
<varname>async</varname> (integer) - Turns on the asynchronous mode for the 'exec_msg' function. All commands - will be executed by a different process and the caller will continue - its flow, without waiting for a response. + Turns on the asynchronous mode for the 'exec_msg' and 'exec' functions. + All commands will be executed by a different process and the caller will + continue its flow, without waiting for a response. @@ -217,7 +217,7 @@ exec_dset("ruri-changer.sh '$ct'"); of the command is ignored. - See sip-server/modules/exec/etc/exec.cfg in the source tarball for + See sip-server/modules/exec/etc/exec.cfg in the source tarball for information on usage. Meaning of the parameters is as follows: @@ -332,6 +332,76 @@ exec_getenv("HOSTNAME", "$avp(localhost)");
+
+ + <function moreinfo="none">exec(command, [output], [input], [error],[envavp])</function> + + + Executes an external command. The input is passed to the standard input of the new + process, if specified, and the output is saved in the output variable. + + Meaning of the parameters is as follows: + + + command - command to be executed.It can include + pseudovariables. + + + + output - pseudovariable where to store the output + from the standard output of the process. Keep in mind that if this parameter + is set, the async paramater will not be taken in consideration. + + + + input - String to be passed to the standard input + of the command. The string can be given as a pseudovariable. + + + + error - pseudovariable where to store the error from + the standard error of the process. + + + + envavp - Avp where to store the values for the + environment variables to be passed for the command. The names of the environment + variables will be "OSIPS_EXEC_#" where # will start from 0. For example if you + store 2 values into an avp ("a" and "b") OSIPS_EXEC_0 will contain the first value + and OSIPS_EXEC_1 the second value. + + + + + WARNING: any OpenSIPS pseudo-vars which may contain special bash + characters should be placed inside quotes, e.g. exec("update-stats.sh '$ct'"); + + + WARNING: "input"/"output"/"error" parameters are not designed for a large amount of + data so one should be careful when using them because server could considerably be + slowed down. + + + This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, + LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. + + + <function moreinfo="none">exec</function> usage + +... +$avp(env) = "a"; +$avp(env) = "b"; +exec("ls -l", "$var(out)",, "$avp(env)"); +xlog("The output is $var(out)\n"); +... +$var(input) = "input"; +exec("/home/../myscript.sh",, "this is my $var(input) for exec\n", "$avp(env)"); +... + + +
+ + diff --git a/modules/exec/exec.c b/modules/exec/exec.c index abb6d4f6fe8..0ae41b76621 100644 --- a/modules/exec/exec.c +++ b/modules/exec/exec.c @@ -49,6 +49,7 @@ #include "../../usr_avp.h" #include "../../ut.h" #include "../../trim.h" +#include "../../mod_fix.h" #include "exec.h" #include "kill.h" @@ -106,10 +107,33 @@ int exec_msg(struct sip_msg *msg, char *cmd ) return ret; } +int exec_write_input(FILE** stream, str* input) +{ + if (fwrite(input->s, 1, input->len, *stream) != input->len) { + LM_ERR("failed to write to pipe\n"); + ser_error=E_EXEC; + return -1; + } + + if (ferror(*stream)) { + LM_ERR("writing pipe: %s\n", strerror(errno)); + ser_error=E_EXEC; + return -1; + } + + pclose(*stream); + + return 0; +} + void exec_async_proc(int rank) { - int pid, status; + #define READ 0 + #define WRITE 1 + + int pid, status, fds[2]; exec_cmd_t *cmd, *prev; + FILE* stream; LM_DBG("started asyncronous process with rank %d\n", rank); @@ -120,14 +144,32 @@ void exec_async_proc(int rank) for (cmd = exec_async_list->first; cmd && cmd->pid; cmd = cmd->next); lock_release(exec_async_list->lock); + if (cmd && cmd->input.len && cmd->input.s) { + if (pipe(fds) != 0) { + LM_ERR("failed to create pipe (%d: %s)\n", + errno, strerror(errno)); + } + } + if (cmd) { if ((pid = fork()) < 0) { LM_ERR("failed to fork\n"); } else if (pid) { exec_async_list->active_childs++; cmd->pid = pid; + + if (cmd->input.s && cmd->input.len) { + close(fds[READ]); + stream = fdopen(fds[WRITE], "w"); + exec_write_input(&stream, &cmd->input); + } + schedule_to_kill(pid); } else { + close(fds[WRITE]); + dup2(fds[READ], 0); + close(fds[READ]); + LM_DBG("running command %s (%d)\n", cmd->cmd, getpid()); execl("/bin/sh", "/bin/sh", "-c", cmd->cmd, NULL); @@ -175,14 +217,23 @@ void exec_async_proc(int rank) if (!exec_async_list->first && !exec_async_list->active_childs) usleep(SLEEP_INTERVAL); } + + #undef READ + #undef WRITE + } -int exec_async(struct sip_msg *msg, char *cmd ) +int exec_async(struct sip_msg *msg, char *cmd, str* input) { exec_cmd_t *elem; /* alloc memory for command */ - elem = shm_malloc(sizeof(exec_cmd_t) + strlen(cmd) + 1); + if (input == NULL) + elem = shm_malloc(sizeof(exec_cmd_t) + strlen(cmd) + 1); + else + elem = shm_malloc(sizeof(exec_cmd_t) + strlen(cmd) + 1 + + input->len); + if (!elem) { LM_ERR("no more shm memory\n"); goto error; @@ -191,6 +242,12 @@ int exec_async(struct sip_msg *msg, char *cmd ) elem->cmd = (char *)(elem + 1); memcpy(elem->cmd, cmd, strlen(cmd) + 1); + if (input) { + elem->input.s = (char*)elem->cmd + strlen(cmd) + 1; + memcpy(elem->input.s, input->s, input->len); + elem->input.len = input->len; + } + /* add command in list at the end */ lock_get(exec_async_list->lock); if (exec_async_list->last) { @@ -472,3 +529,119 @@ int exec_getenv(struct sip_msg *msg, char *cmd, pvname_list_p avpl) error: return ret; } + + +static int read_and_write2var(struct sip_msg* msg, FILE** strm, gparam_p outvar) +{ + #define MAX_LINE_SIZE 1024 + #define MAX_BUF_SIZE 32 * MAX_LINE_SIZE + + int buflen=0, tmplen; + pv_value_t outval; + char buf[MAX_BUF_SIZE], tmpbuf[MAX_LINE_SIZE]; + + while((tmplen=fread(tmpbuf, 1, MAX_LINE_SIZE, *strm))) { + if ((buflen + tmplen) >= MAX_BUF_SIZE) { + LM_WARN("no more space in output buffer\n"); + break; + } + memcpy(buf+buflen, tmpbuf, tmplen); + buflen += tmplen; + + outval.flags = PV_VAL_STR; + outval.rs.s = buf; + outval.rs.len = buflen; + + if (buflen && + pv_set_value(msg, &outvar->v.pve->spec, 0, &outval) < 0) { + LM_ERR("cannot set output pv value\n"); + return -1; + } + } + + return 0; + + #undef MAX_LINE_SIZE + #undef MAX_BUF_SIZE +} + +int exec_sync(struct sip_msg* msg, str* command, str* input, gparam_p outvar, gparam_p errvar) +{ + + pid_t pid; + int exit_status, ret; + FILE *pin, *pout, *perr; + + if (input || outvar || errvar) { + pid = ___popen(command->s, input ? &pin : NULL, + outvar ? &pout : NULL, + errvar ? &perr : NULL); + } else { + pid = fork(); + if (pid == 0) { + execl("/bin/sh", "/bin/sh", "-c", command->s, NULL); + exit(-1); + } + } + + if (input->len) { + if (fwrite(input->s, 1, input->len, pin) != input->len) { + LM_ERR("failed to write to pipe\n"); + ser_error=E_EXEC; + goto error; + } + + if (ferror(pin)) { + ser_error=E_EXEC; + goto error; + } + pclose(pin); + } + + schedule_to_kill(pid); + wait(&exit_status); + + if (outvar) { + if (read_and_write2var(msg, &pout, outvar) < 0) { + LM_ERR("failed reading from pipe\n"); + return -1; + } + } + + if (errvar) { + if (read_and_write2var(msg, &perr, errvar) < 0) { + LM_ERR("failed reading stderr from pipe\n"); + return -1; + } + } + + ret=1; + +error: + if (outvar && ferror(pout)) { + LM_ERR("reading pipe: %s\n", strerror(errno)); + ser_error=E_EXEC; + ret=-1; + } + + if (errvar && ferror(perr)) { + LM_ERR("err pipe: %s\n", strerror(errno)); + ser_error=E_EXEC; + ret=-1; + } + + if (outvar) + pclose(pout); + if (errvar) + pclose(perr); + + if (WIFEXITED(exit_status)) { + if (WEXITSTATUS(exit_status)!=0) ret=-1; + } else { + LM_ERR("cmd %s failed. exit_status=%d, errno=%d: %s\n", + command->s, exit_status, errno, strerror(errno)); + ret=-1; + } + + return ret; +} diff --git a/modules/exec/exec.h b/modules/exec/exec.h index 72ad6e66f36..77bec619de2 100644 --- a/modules/exec/exec.h +++ b/modules/exec/exec.h @@ -30,6 +30,7 @@ typedef struct _exec_cmd { char *cmd; + str input; int pid; struct _exec_cmd *next; } exec_cmd_t; @@ -45,12 +46,13 @@ extern exec_list_p exec_async_list; /* process that waits for asyncronous executions */ void exec_async_proc(int rank); -int exec_async(struct sip_msg *msg, char *cmd ); +int exec_async(struct sip_msg *msg, char *cmd, str* input ); int exec_str(struct sip_msg *msg, char *cmd, char *param, int param_len); int exec_msg(struct sip_msg *msg, char *cmd ); int exec_avp(struct sip_msg *msg, char *cmd, pvname_list_p avpl); int exec_getenv(struct sip_msg *msg, char *cmd, pvname_list_p avpl); +int exec_sync(struct sip_msg* msg, str* command, str* input, gparam_p outvar, gparam_p errvar); #endif diff --git a/modules/exec/exec.so b/modules/exec/exec.so new file mode 100755 index 00000000000..58e5aab9ea1 Binary files /dev/null and b/modules/exec/exec.so differ diff --git a/modules/exec/exec_hf.c b/modules/exec/exec_hf.c index aadaa87e7c6..fddd11df94d 100644 --- a/modules/exec/exec_hf.c +++ b/modules/exec/exec_hf.c @@ -92,7 +92,7 @@ static int insert_hf( struct hf_wrapper **list, struct hdr_field *hf ) return 1; } -static void release_hf_struct( struct hf_wrapper *list ) +void release_hf_struct( struct hf_wrapper *list ) { struct hf_wrapper *i, *j, *nexts, *nexto; @@ -287,7 +287,7 @@ static int print_var(struct hf_wrapper *w, int offset) } } -static void release_vars(struct hf_wrapper *list) +void release_vars(struct hf_wrapper *list) { while(list) { if (list->envvar) { diff --git a/modules/exec/exec_hf.h b/modules/exec/exec_hf.h index f68621bb32a..7c368dd5156 100644 --- a/modules/exec/exec_hf.h +++ b/modules/exec/exec_hf.h @@ -117,5 +117,8 @@ extern char **environ; environment_t *set_env(struct sip_msg *msg); void unset_env(environment_t *backup_env); +void release_hf_struct ( struct hf_wrapper *list); +void release_vars(struct hf_wrapper* list); +environment_t *replace_env(struct hf_wrapper *list); #endif diff --git a/modules/exec/exec_mod.c b/modules/exec/exec_mod.c index 523f0c8c7ee..8b63698fc63 100644 --- a/modules/exec/exec_mod.c +++ b/modules/exec/exec_mod.c @@ -29,6 +29,7 @@ #include +#include #include "../../sr_module.h" #include "../../parser/msg_parser.h" @@ -36,6 +37,8 @@ #include "../../dprint.h" #include "../../mod_fix.h" #include "../../parser/parse_uri.h" +#include "../../ut.h" +#include #include "exec.h" #include "kill.h" @@ -43,6 +46,7 @@ + unsigned int time_to_kill=0; unsigned int async=0; @@ -52,8 +56,10 @@ inline static int w_exec_dset(struct sip_msg* msg, char* cmd, char* foo); inline static int w_exec_msg(struct sip_msg* msg, char* cmd, char* foo); inline static int w_exec_avp(struct sip_msg* msg, char* cmd, char* avpl); inline static int w_exec_getenv(struct sip_msg* msg, char* cmd, char* avpl); +inline static int w_exec(struct sip_msg* msg, char* cmd, char* out, char* in, char* err, char* avp_env); static int exec_avp_fixup(void** param, int param_no); +static int exec_fixup(void** param, int param_no); inline static void exec_shutdown(void); @@ -71,6 +77,8 @@ static cmd_export_t cmds[] = { REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec_getenv", (cmd_function)w_exec_getenv, 2, exec_avp_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, + {"exec", (cmd_function)w_exec, 5, exec_fixup, 0, + REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {0, 0, 0, 0, 0, 0} }; @@ -115,12 +123,15 @@ struct module_exports exports= { void exec_shutdown(void) { + if (time_to_kill) destroy_kill(); + } static int mod_init( void ) { + LM_INFO("exec - initializing\n"); if (time_to_kill) initialize_kill(); @@ -220,7 +231,7 @@ inline static int w_exec_msg(struct sip_msg* msg, char* cmd, char* foo) LM_DBG("executing [%s]\n", command.s); if(async) - ret=exec_async(msg, command.s); + ret=exec_async(msg, command.s, NULL); else ret=exec_msg(msg, command.s); if (setvars) { @@ -315,4 +326,204 @@ static int exec_avp_fixup(void** param, int param_no) return 0; } +static int exec_fixup(void** param, int param_no) +{ + gparam_p out_var; + pv_elem_t* model; + str s; + + if (*param) + switch (param_no) { + case 1: /* cmd */ + return fixup_spve(param); + case 2: /* output var */ + case 4: /* error var */ + if (fixup_spve(param)) { + LM_ERR("cannot fix output var\n"); + return -1; + } + + out_var = *param; + if (out_var->type != GPARAM_TYPE_PVE) { + LM_ERR("output var must be a single variable\n"); + return -1; + } + + if (out_var->v.pve->spec.setf == NULL) { + LM_ERR("output var must be writable\n"); + return -1; + } + + return 0; + case 3: /* input vars */ + s.s = *param; + s.len = strlen(s.s); + if (pv_parse_format(&s, &model)) { + LM_ERR("wrong format [%s] for param no %d!\n", + (char*)*param, param_no); + pkg_free(s.s); + return E_UNSPEC; + } + *param = (void *)model; + + return 0; + case 5: /* environment avp */ + if (fixup_spve(param)) { + LM_ERR("cannot fix output var\n"); + return -1; + } + out_var = *param; + if (out_var->type != GPARAM_TYPE_PVE) { + LM_ERR("env var must be a single variable\n"); + return -1; + } + + if (out_var->v.pve->spec.type != PVT_AVP) { + LM_ERR("env var must be avp typed\n"); + return -1; + } + + return 0; + default: + LM_ERR("Invalid parameter number %d\n", param_no); + return -1; + } + return 0; +} + +static inline int setenvvar(struct hf_wrapper** hf, int_str* value, int idx) +{ + #define OSIPS_EXEC "OSIPS_EXEC_" + + + int len=0; + str sidx; + + sidx.s = int2str((unsigned long)idx, &sidx.len); + + (*hf)->envvar=pkg_malloc(strlen(OSIPS_EXEC) + sidx.len + 1/*=*/ + + (*value).s.len + 1/*\0*/); + if ((*hf)->envvar==0) { + LM_ERR("no more pkg mem\n"); + return -1; + } + + memcpy((*hf)->envvar, OSIPS_EXEC, strlen(OSIPS_EXEC)); + len=strlen(OSIPS_EXEC); + + memcpy((*hf)->envvar+len, sidx.s, sidx.len); + len+=sidx.len; + + (*hf)->envvar[len++] = '='; + + memcpy((*hf)->envvar+len, (*value).s.s, (*value).s.len); + + (*hf)->envvar[len+(*value).s.len] = '\0'; + + (*hf)->next_other=(*hf)->next_same=NULL; + + return 0; + + #undef OSIPS_EXEC + +} + +static struct hf_wrapper* get_avp_values_list(struct sip_msg* msg, pv_param_p avp) +{ + + int avp_name, idx=0; + unsigned short name_type; + int_str value; + struct usr_avp* avp_ptr=0; + struct hf_wrapper *hf=0, *hf_head; + + if (pv_get_avp_name(msg, avp, &avp_name, &name_type) < 0) { + LM_ERR("cannot get avp name\n"); + return 0; + } + + if ((avp_ptr=search_first_avp( name_type, avp_name, &value, 0)) == 0) { + LM_ERR("cannot get first avp value\n"); + return 0; + } + + hf=pkg_malloc(sizeof(struct hf_wrapper)); + if (!hf) + goto memerr; + + setenvvar(&hf, &value, idx++); + hf_head=hf; + + while (search_next_avp( avp_ptr, &value) != 0) { + hf->next_other=pkg_malloc(sizeof(struct hf_wrapper)); + if (!hf) + goto memerr; + + hf=hf->next_other; + + setenvvar(&hf, &value, idx++); + + avp_ptr = avp_ptr->next; + if (avp_ptr->id > avp_name) + break; + } + + return hf_head; +memerr: + LM_ERR("no more pkg mem\n"); + return 0; + +} + + +static int w_exec(struct sip_msg* msg, char* cmd, char* out, char* in, char* err ,char* avp_env) +{ + + str command; + str input = {NULL, 0}; + int ret; + struct hf_wrapper *hf=0; + environment_t* backup_env=0; + gparam_p outvar = (gparam_p)out; + gparam_p errvar = (gparam_p)err; + + if (msg == 0 || cmd == 0) + return -1; + + /* fetch command */ + if(fixup_get_svalue(msg, (gparam_p)cmd, &command)!=0) { + LM_ERR("invalid command parameter"); + return -1; + } + + /* fetch input */ + if (in != NULL) { + if (pv_printf_s(msg, (pv_elem_p)in, &input)!=0) + return -1; + } + + if (avp_env != NULL) { + if ((hf=get_avp_values_list(msg, &((gparam_p)avp_env)->v.pve->spec.pvp)) == 0) + return -1; + backup_env=replace_env(hf); + if (!backup_env) { + LM_ERR("replace env failed\n"); + release_vars(hf); + release_hf_struct(hf); + return -1; + } + release_hf_struct(hf); + } + + if (!out && async) { + ret = exec_async(msg, command.s, &input); + } else { + ret = exec_sync(msg, &command, &input, outvar, errvar); + } + + if (backup_env) + unset_env(backup_env); + + return ret; +} diff --git a/modules/exec/kill.c b/modules/exec/kill.c index 3134c7e3fea..8b15ac738e9 100644 --- a/modules/exec/kill.c +++ b/modules/exec/kill.c @@ -164,6 +164,81 @@ pid_t __popen(const char *cmd, const char *type, FILE **stream) return ret; } +pid_t ___popen(const char* cmd, FILE** strm_w, FILE** strm_r, FILE** strm_e) +{ +#define OPEN_PIPE(strm, fds) \ + do { \ + if (strm) { \ + if (pipe(fds) != 0) { \ + LM_ERR("failed to create reading pipe (%d: %s)\n", \ + errno, strerror(errno)); \ + return -1; \ + } \ + } \ + } while (0); + +/* + * cl - pipe end to be closed + * op - pipe end to be redirected + * re - fds where to redirect 'op' + */ +#define CLOSE_AND_REDIRECT(strm, fds, cl, op, re) \ + do { \ + if (strm) { \ + close(fds[cl]); \ + dup2(fds[op], re); \ + close(fds[op]); \ + } \ + } while (0); + +#define CLOSE_END_AND_OPEN_STREAM(strm, way, fds, end2close) \ + do { \ + if (strm) { \ + close(fds[end2close]); \ + *strm = fdopen(fds[(1^end2close)], way); \ + } \ + }while (0); + + pid_t ret; + int r_fds[2], w_fds[2], e_fds[2]; + + if (strm_r == NULL && strm_w == NULL && strm_e == NULL) { + LM_WARN("no descriptor redirect required\n"); + } + + OPEN_PIPE(strm_w, w_fds); + OPEN_PIPE(strm_r, r_fds); + OPEN_PIPE(strm_e, e_fds); + + ret=fork(); + + if (ret==0) { + /* write pipe */ + CLOSE_AND_REDIRECT(strm_w, w_fds, 1, 0 ,0); + + /* read pipe */ + CLOSE_AND_REDIRECT(strm_r, r_fds, 0, 1, 1); + + /* error pipe */ + CLOSE_AND_REDIRECT(strm_e, e_fds, 0, 1, 2); + + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + + exit(-1); + } + + CLOSE_END_AND_OPEN_STREAM(strm_w, "w", w_fds, 0); + CLOSE_END_AND_OPEN_STREAM(strm_r, "r", r_fds, 1); + CLOSE_END_AND_OPEN_STREAM(strm_e, "r", e_fds, 1); + + return ret; + +#undef OPEN_PIPE +#undef CLOSE_AND_REDIRECT +#undef CLOSE_AND_OPEN_STREAM + +} + int schedule_to_kill( int pid ) { struct timer_link *tl; diff --git a/modules/exec/kill.h b/modules/exec/kill.h index e519d80ed98..cdc2be42f75 100644 --- a/modules/exec/kill.h +++ b/modules/exec/kill.h @@ -51,6 +51,7 @@ int schedule_to_kill( int pid ); * @stream: stream to be returned to the caller */ pid_t __popen(const char *cmd, const char *type, FILE **stream); +pid_t ___popen(const char *cmd, FILE **, FILE**, FILE**); #endif diff --git a/modules/ldap/README b/modules/ldap/README index 3b59c2e1af0..43e0237de9e 100644 --- a/modules/ldap/README +++ b/modules/ldap/README @@ -84,14 +84,18 @@ Christian Schlatter 1.4. ldap_bind_password example 1.5. ldap_network_timeout example 1.6. ldap_client_bind_timeout example - 1.7. Example LDAP Configuration File - 1.8. config_file parameter usage - 1.9. Example Usage of ldap_url - 1.10. Example Usage - 1.11. Example Usage - 1.12. Example Usage - 1.13. Example Usage + 1.7. ldap_ca_cert_file example + 1.8. ldap_cert_file example + 1.9. ldap_key_file example + 1.10. ldap_require_certificate example + 1.11. Example LDAP Configuration File + 1.12. config_file parameter usage + 1.13. Example Usage of ldap_url 1.14. Example Usage + 1.15. Example Usage + 1.16. Example Usage + 1.17. Example Usage + 1.18. Example Usage 2.1. Example code fragment to load LDAP module API 2.2. Example LDAP module API function call @@ -114,6 +118,7 @@ Chapter 1. Admin Guide * Configurable LDAP connection and bind timeouts * Module API for LDAP search operations that can be used by other OpenSIPS modules + * StartTLS support The module implementation makes use of the open source OpenLDAP library available on most UNIX/Linux platforms. Besides LDAP @@ -278,6 +283,12 @@ ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 +ldap_ca_cert_file = "/usr/share/ca-certificates/mycert. +pem" +ldap_cert_file = "/var/my-certificate/certificate.pe +m" +ldap_key_file = "/var/my-certificate/key.pem" +ldap_require_certificate = "ALLOW" The configuration keys are explained in the following section. This LDAP session can be referred to in the routing script by @@ -354,13 +365,54 @@ ldap_network_timeout = 500 ; setting TCP timeout to 500 ms ldap_client_bind_timeout = 1000 + ldap_ca_cert_file (optional) + LDAP full path of the CA certificate file. + + No default value. It is mandatory in case you wish to + use StartTLS + + Example 1.7. ldap_ca_cert_file example + +ldap_ca_cert_file = "/usr/local/CAcert.pem" + + ldap_cert_file (optional) + LDAP full path of the certificate file. + + No default value. It is mandatory in case you wish to + use StartTLS + + Example 1.8. ldap_cert_file example + +ldap_cert_file = "/usr/local/mycert.pem" + + ldap_key_file (optional) + LDAP full path of the key file. + + No default value. It is mandatory in case you wish to + use StartTLS + + Example 1.9. ldap_key_file example + +ldap_key_file = "/usr/local/mykey.pem" + + ldap_require_certificate (optional) + LDAP peer certificate checking strategy, one of "NEVER", + "HARD", "DEMAND", "ALLOW", "TRY". Lower case letters are + also accepted. + + Default value "NEVER". + + Example 1.10. ldap_require_certificate example + +ldap_require_certificate = "NEVER" + 1.3.3. Configuration File Example The following configuration file example includes two LDAP session definitions that could be used e.g. for accessing H.350 data and do phone number to name mappings. - Example 1.7. Example LDAP Configuration File + Example 1.11. Example LDAP Configuration File # LDAP session "sipaccounts": # # - using LDAPv3 (default) @@ -372,6 +424,11 @@ ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com" ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 +#using StartTLS +ldap_ca_cert_file = "/ldap/path/to/ca/certificate.pem" +ldap_cert_file = "/ldap/path/to/certificate.pem" +ldap_key_file = "/ldap/path/to/key/file.pem" +ldap_require_certificate = "NEVER" # LDAP session "campus": @@ -393,7 +450,7 @@ ldap_client_bind_timeout = 500 Default value: /usr/local/etc/opensips/ldap.cfg - Example 1.8. config_file parameter usage + Example 1.12. config_file parameter usage modparam("ldap", "config_file", "/etc/opensips/ldap.ini") 1.5. Exported Functions @@ -419,7 +476,7 @@ modparam("ldap", "config_file", "/etc/opensips/ldap.ini") OpenSIPS pseudo variables and AVPs included in ldap_url do get substituted with their value. - Example 1.9. Example Usage of ldap_url + Example 1.13. Example Usage of ldap_url Search with LDAP session named sipaccounts, base ou=sip,dc=example,dc=com, one level deep using search @@ -453,7 +510,7 @@ ldap://ldap_1/dc=example,dc=com? This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - Example 1.10. Example Usage + Example 1.14. Example Usage ... # ldap search if (!ldap_search("ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=$ @@ -537,7 +594,7 @@ regex_subst]) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - Example 1.11. Example Usage + Example 1.15. Example Usage ... # ldap_search call @@ -610,7 +667,7 @@ regex_subst]) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - Example 1.12. Example Usage + Example 1.16. Example Usage ... # ldap_search call ... @@ -660,7 +717,7 @@ if (!ldap_result_check("sn/$ru", "/^sip:([^@]).*$/\1/")) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - Example 1.13. Example Usage + Example 1.17. Example Usage ... # ldap_search call ... @@ -719,7 +776,7 @@ if (ldap_result_next()) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - Example 1.14. Example Usage + Example 1.18. Example Usage ... if (!ldap_filter_url_encode("cn=$avp(name)", "$avp(name_esc)")) { diff --git a/modules/ldap/doc/ldap_admin.xml b/modules/ldap/doc/ldap_admin.xml index 28e94f436be..7f0ac734b45 100644 --- a/modules/ldap/doc/ldap_admin.xml +++ b/modules/ldap/doc/ldap_admin.xml @@ -5,7 +5,7 @@ Overview The LDAP module implements an LDAP search interface for OpenSIPS. It exports script functions to perform an LDAP search operation and to store the search results as OpenSIPS AVPs. This allows for using LDAP directory data in the OpenSIPS SIP message routing script. - + The following features are offered by the LDAP module: @@ -29,12 +29,15 @@ Module API for LDAP search operations that can be used by other OpenSIPS modules - - + + StartTLS support + +
+ The module implementation makes use of the open source OpenLDAP library available on most UNIX/Linux platforms. Besides LDAP server failover and automatic reconnect, this module can handle multiple LDAP sessions concurrently allowing to access data stored on different LDAP servers. Each OpenSIPS worker process maintains one LDAP TCP connection per configured LDAP server. This enables parallel execution of LDAP requests and offloads LDAP concurrency control to the LDAP server(s). - - An LDAP search module API is provided that can be used by other OpenSIPS modules. A module using this API does not have to implement LDAP connection management and configuration, while still having access to the full OpenLDAP API for searching and result handling. - + + An LDAP search module API is provided that can be used by other OpenSIPS modules. A module using this API does not have to implement LDAP connection management and configuration, while still having access to the full OpenLDAP API for searching and result handling. + Since LDAP server implementations are optimized for fast read access they are a good choice to store SIP provisioning data. Performance tests have shown that this module achieves lower data access times and higher call rates than other database modules like e.g. the OpenSIPS MYSQL module.
@@ -47,11 +50,11 @@ The ldap_search function () performs an LDAP search operation. It expects an LDAP URL as input which includes the LDAP session name and search parameters. provides a quick overview on LDAP URLs. - + The result of an LDAP search is stored internally and can be accessed with one of the ldap_result* functions. ldap_result () stores resulting LDAP attribute value as AVPs. ldap_result_check () is a convenience function to compare a string with LDAP attribute values using regular expression matching. Finally, ldap_result_next () allows to handle LDAP search queries that return more than one LDAP entry. - + All ldap_result* functions do always access the LDAP result set from the last ldap_search call. This should be kept in mind when calling ldap_search more than once in the OpenSIPS configuration script. @@ -160,10 +163,10 @@ Non-URL characters in an LDAP URL have to be escaped using - percent-encoding (refer to section 2.1 of RFC 4516). In particular + percent-encoding (refer to section 2.1 of RFC 4516). In particular this means that any "?" character in an LDAP URL component must be written as "%3F", since "?" is used as a URL delimiter. - The exported function ldap_filter_url_encode () + The exported function ldap_filter_url_encode () implements RFC 4515/4516 LDAP search filter and URL escaping rules. @@ -231,6 +234,10 @@ ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com" ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 +ldap_ca_cert_file = "/usr/share/ca-certificates/mycert.pem" +ldap_cert_file = "/var/my-certificate/certificate.pem" +ldap_key_file = "/var/my-certificate/key.pem" +ldap_require_certificate = "ALLOW" The configuration keys are explained in the following section. This LDAP session can be referred @@ -259,7 +266,7 @@ ldap_client_bind_timeout = 500 ldap_server_url = "ldap://localhost" ldap_server_url = "ldap://ldap.example.com:7777" -ldap_server_url = "ldap://ldap1.example.com, +ldap_server_url = "ldap://ldap1.example.com, ldap://ldap2.example.com:80389" @@ -353,6 +360,75 @@ ldap_server_url = "ldap://ldap1.example.com, + + ldap_ca_cert_file (optional) + + + LDAP full path of the CA certificate file. + + No default value. It is mandatory in case you wish to use StartTLS + + + <varname>ldap_ca_cert_file</varname> + example + + ldap_ca_cert_file = "/usr/local/CAcert.pem" + + + + + + ldap_cert_file (optional) + + + LDAP full path of the certificate file. + + No default value. It is mandatory in case you wish to use StartTLS + + + <varname>ldap_cert_file</varname> + example + + ldap_cert_file = "/usr/local/mycert.pem" + + + + + + ldap_key_file (optional) + + + LDAP full path of the key file. + + No default value. It is mandatory in case you wish to use StartTLS + + + <varname>ldap_key_file</varname> + example + + ldap_key_file = "/usr/local/mykey.pem" + + + + + + ldap_require_certificate (optional) + + + LDAP peer certificate checking strategy, one of "NEVER", "HARD", "DEMAND", "ALLOW", "TRY". + Lower case letters are also accepted. + + Default value "NEVER". + + + <varname>ldap_require_certificate</varname> + example + + ldap_require_certificate = "NEVER" + + + +
@@ -378,6 +454,11 @@ ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com" ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 +#using StartTLS +ldap_ca_cert_file = "/ldap/path/to/ca/certificate.pem" +ldap_cert_file = "/ldap/path/to/certificate.pem" +ldap_key_file = "/ldap/path/to/key/file.pem" +ldap_require_certificate = "NEVER" # LDAP session "campus": @@ -523,7 +604,7 @@ ldap://ldap_1/dc=example,dc=com? This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - + Example Usage @@ -568,9 +649,9 @@ ldap_result("telephoneNumber/$avp(tel_number)"); part of an attribute value should be stored as AVP.
- An AVP can either be of type string or integer. As default, ldap_result stores LDAP attribute values as AVP of type string. The optional avp_type parameter can be used to explicitly specify the type of the AVP. It can be either str for string, or int for integer. If avp_type is specified as int then ldap_result tries to convert the LDAP attribute values to integer. In this case, the values are only stored as AVP if the conversion to integer is succesfull. + An AVP can either be of type string or integer. As default, ldap_result stores LDAP attribute values as AVP of type string. The optional avp_type parameter can be used to explicitly specify the type of the AVP. It can be either str for string, or int for integer. If avp_type is specified as int then ldap_result tries to convert the LDAP attribute values to integer. In this case, the values are only stored as AVP if the conversion to integer is succesfull. - + Function Parameters: @@ -593,7 +674,7 @@ ldap_result("telephoneNumber/$avp(tel_number)"); $avp(12) - + avp_type @@ -650,7 +731,7 @@ ldap_result("telephoneNumber/$avp(tel_number)"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - + Example Usage @@ -673,7 +754,7 @@ if (!ldap_result("SIPIdentityServiceLevel/$avp(service_level)")) # internal error sl_send_reply("500", "Internal server error"); exit; - default: + default: exit; } } @@ -767,7 +848,7 @@ ldap_result("SIPIdentitySIPURI/$avp(10)", "/^[^@]+@(.+)$/\1/"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. - + Example Usage @@ -861,7 +942,7 @@ if (ldap_result_next()) if (ldap_result_next()) { ldap_result("telephonenumber/$avp(tel4)"); -} +} ... @@ -873,7 +954,7 @@ if (ldap_result_next()) This function applies the following escaping rules to string and stores the result in AVP avp_spec: - + ldap_filter_url_encode() escaping rules @@ -934,7 +1015,7 @@ if (ldap_result_next())
The string stored in AVP avp_spec can be safely used in an LDAP - URL filter string. + URL filter string. Function Parameters: @@ -1000,7 +1081,7 @@ if (!ldap_filter_url_encode("cn=$avp(name)", "$avp(name_esc)")) xlog("L_INFO", "encoded LDAP filter component: [$avp(name_esc)]\n"); if (ldap_search( - "ldap://h350/ou=commObjects,dc=example,dc=com??sub?($avp(name_esc))")) + "ldap://h350/ou=commObjects,dc=example,dc=com??sub?($avp(name_esc))")) { ... } ... diff --git a/modules/ldap/ld_session.c b/modules/ldap/ld_session.c index 6fc484db288..1872aa3f29a 100644 --- a/modules/ldap/ld_session.c +++ b/modules/ldap/ld_session.c @@ -43,7 +43,8 @@ int add_ld_session(char* _name, LDAP* _ldh, dictionary* _d) { struct ld_session* current = ld_sessions; struct ld_session* new_lds = NULL; - char *host_name, *bind_dn, *bind_pwd; + char *host_name, *bind_dn, *bind_pwd, *bind_cacert, *bind_cert, *bind_key, + *bind_req_cert; int client_search_timeout_ms, client_bind_timeout_ms, network_timeout_ms; new_lds = (struct ld_session*)pkg_malloc(sizeof(struct ld_session)); @@ -139,6 +140,59 @@ int add_ld_session(char* _name, LDAP* _ldh, dictionary* _d) memset(new_lds->bind_pwd, 0, strlen(bind_pwd)+1); strcpy(new_lds->bind_pwd, bind_pwd); + /* bind_cacert*/ + bind_cacert = iniparser_getstring( + _d, + get_ini_key_name(_name, CFG_N_LDAP_CACERTFILE), + CFG_DEF_LDAP_CACERTFILE); + new_lds->cacertfile = (char*)pkg_malloc(strlen(bind_cacert)+1); + if (new_lds->cacertfile == NULL) { + LM_ERR("no memory\n"); + return -1; + } + memset(new_lds->cacertfile, 0, strlen(bind_cacert)+1); + strcpy(new_lds->cacertfile, bind_cacert); + + /* bind_certfile */ + bind_cert = iniparser_getstring( + _d, + get_ini_key_name(_name, CFG_N_LDAP_CERTFILE), + CFG_DEF_LDAP_CERTFILE); + new_lds->certfile = (char*)pkg_malloc(strlen(bind_cert)+1); + if (new_lds->certfile == NULL) { + LM_ERR("no memory\n"); + return -1; + } + memset(new_lds->certfile, 0, strlen(bind_cert)+1); + strcpy(new_lds->certfile, bind_cert); + + /* bind_key */ + bind_key = iniparser_getstring( + _d, + get_ini_key_name(_name, CFG_N_LDAP_KEYFILE), + CFG_DEF_LDAP_KEYFILE); + new_lds->keyfile = (char*)pkg_malloc(strlen(bind_key)+1); + if (new_lds->keyfile == NULL) { + LM_ERR("no memory\n"); + return -1; + } + memset(new_lds->keyfile, 0, strlen(bind_key)+1); + strcpy(new_lds->keyfile, bind_key); + + /* bind_req_cert */ + bind_req_cert = iniparser_getstring( + _d, + get_ini_key_name(_name, CFG_N_LDAP_REQCERT), + CFG_DEF_LDAP_REQCERT); + new_lds->req_cert = (char*)pkg_malloc(strlen(bind_req_cert)+1); + if (new_lds->req_cert == NULL) { + LM_ERR("no memory\n"); + return -1; + } + memset(new_lds->req_cert, 0, strlen(bind_req_cert)+1); + strcpy(new_lds->req_cert, bind_req_cert); + + /* calculate_ha1 */ new_lds->calculate_ha1 = iniparser_getboolean( _d, @@ -146,6 +200,7 @@ int add_ld_session(char* _name, LDAP* _ldh, dictionary* _d) CFG_DEF_CALCULATE_HA1); + if (current == NULL) { ld_sessions = new_lds; diff --git a/modules/ldap/ld_session.h b/modules/ldap/ld_session.h index e197fe48953..6f5f6b530d8 100644 --- a/modules/ldap/ld_session.h +++ b/modules/ldap/ld_session.h @@ -50,6 +50,10 @@ struct ld_session { char* bind_dn; char* bind_pwd; int calculate_ha1; + char* cacertfile; + char* certfile; + char* keyfile; + char* req_cert; struct ld_session* next; }; @@ -63,6 +67,11 @@ struct ld_session { #define CFG_N_LDAP_BIND_PWD "ldap_bind_password" #define CFG_N_CALCULATE_HA1 "calculate_ha1" +#define CFG_N_LDAP_CACERTFILE "ldap_ca_cert_file" +#define CFG_N_LDAP_CERTFILE "ldap_cert_file" +#define CFG_N_LDAP_KEYFILE "ldap_key_file" +#define CFG_N_LDAP_REQCERT "ldap_require_certificate" + #define CFG_DEF_HOST_NAME "" #define CFG_DEF_LDAP_SERVER_URL NULL @@ -76,6 +85,11 @@ struct ld_session { #define CFG_LDAP_CLIENT_SEARCH_TIMEOUT_MIN 2000 +#define CFG_DEF_LDAP_CACERTFILE "" +#define CFG_DEF_LDAP_CERTFILE "" +#define CFG_DEF_LDAP_KEYFILE "" +#define CFG_DEF_LDAP_REQCERT "NEVER" + extern int add_ld_session(char* _name, LDAP* _ldh, dictionary* _d); extern struct ld_session* get_ld_session(char* _name); extern int free_ld_sessions(); diff --git a/modules/ldap/ldap_connect.c b/modules/ldap/ldap_connect.c index 55bb6eebb0b..4c7fbf45040 100644 --- a/modules/ldap/ldap_connect.c +++ b/modules/ldap/ldap_connect.c @@ -42,10 +42,83 @@ #include "../../mem/mem.h" #include "../../ut.h" +static inline int ldap_word2upper(char* input) +{ + int index=0; + + while (input[index] == ' ') + input++; + + while (input[index] != '\0') { + if (input[index] >= 'a' && input[index] <= 'z') { + input[index++] -= 32; + continue; + } + + if (input[index] <= 'A' && input[index] >= 'Z') { + LM_ERR("invalid req_cert parameter!" + " must contain only letters\n"); + return -1; + } + + index++; + } + + return 0; +} + +static inline int get_req_cert_param(char* req_cert) +{ + #define DWORD(s) ( *s + (*(s+1) << 8) + (*(s+2) << 16) + (*(s+3) << 24)) + + switch (DWORD(req_cert)) { + case NEVE: + if (*(req_cert+4) != 'R' || *(req_cert+5) != '\0') + goto error; + return LDAP_OPT_X_TLS_NEVER; + case DEMA: + if (*(req_cert+4) != 'N' || *(req_cert+5) != 'D' || + *(req_cert+6) != '\0') + goto error; + return LDAP_OPT_X_TLS_DEMAND; + case ALLO: + if (*(req_cert+4) != 'W' || *(req_cert+5) != '\0') + goto error; + return LDAP_OPT_X_TLS_ALLOW; + case HARD: + if (*(req_cert+4) != '\0') + goto error; + return LDAP_OPT_X_TLS_HARD; + case TRY: + return LDAP_OPT_X_TLS_TRY; + default : + goto error; + + } + +error: + LM_ERR("invalid req_cert parameter [%s]!" + "OPTIONS: NEVER|DEMAND|ALLOW|HARD|TRY\n", req_cert); + return -1; + #undef DWORD +} + + int ldap_connect(char* _ld_name) { + #define W_SET_OPTION(handle, opt, str, name) \ + do { \ + if (ldap_set_option( handle, opt, str) \ + != LDAP_OPT_SUCCESS) { \ + LM_ERR("[%s]: could not set " # opt " [%s]\n" \ + , name, str); \ + return -1; \ + } \ + } while (0); + int rc; int ldap_proto_version; + int req_cert_value; struct ld_session* lds; struct berval ldap_cred; struct berval* ldap_credp; @@ -177,6 +250,47 @@ int ldap_connect(char* _ld_name) ldap_credp = &ldap_cred; } + /* configure tls */ + if (*lds->cacertfile && *lds->certfile && *lds->keyfile) { + + W_SET_OPTION(lds->handle, LDAP_OPT_X_TLS_CACERTFILE, + lds->cacertfile, _ld_name); + + W_SET_OPTION(lds->handle, LDAP_OPT_X_TLS_CERTFILE, + lds->certfile, _ld_name); + + W_SET_OPTION(lds->handle, LDAP_OPT_X_TLS_KEYFILE, + lds->keyfile, _ld_name); + + if (ldap_word2upper(lds->req_cert) != 0) + return -1; + + if ((req_cert_value = get_req_cert_param(lds->req_cert)) < 0) + return -1; + + if (ldap_set_option( lds->handle, LDAP_OPT_X_TLS_REQUIRE_CERT, + &req_cert_value) != LDAP_OPT_SUCCESS) { + LM_ERR("[%s]: could not set LDAP_OPT_X_TLS_REQUIRE_CERT [%s]\n" + , _ld_name, lds->req_cert); + return -1; + } + + int ret = + ldap_start_tls_s(lds->handle, NULL, NULL); + if (ret != LDAP_SUCCESS) { + LM_ERR("ERROR %d %s\n", ret, ldap_err2string(ret)); + LM_ERR("cannot start tls! Check certificates and" + " keyfile path and access rights\n"); + return -1; + } + + LM_INFO("Using StartTLS for session [%s]\n", _ld_name); + } else if (*lds->cacertfile || *lds->certfile || *lds->keyfile) { + LM_WARN("ldap_ca_certfile, ldap_cert_file and ldap_key_file" + " must be set in order to use StartTLS. " + "No StartTLS configured!\n"); + } + /* * ldap_sasl_bind (LDAP_SASL_SIMPLE) */ diff --git a/modules/ldap/ldap_connect.h b/modules/ldap/ldap_connect.h index 898f4e22b7f..1be4578eec2 100644 --- a/modules/ldap/ldap_connect.h +++ b/modules/ldap/ldap_connect.h @@ -36,6 +36,12 @@ #include "../../str.h" #include "../../dprint.h" +#define NEVE 0x4556454E +#define DEMA 0x414D4544 +#define ALLO 0x4F4C4C41 +#define HARD 0x44524148 +#define TRY 0x00595254 + extern int ldap_connect(char* _ld_name); extern int ldap_disconnect(char* _ld_name); extern int ldap_reconnect(char* _ld_name); diff --git a/modules/ldap/ldap_mod.c b/modules/ldap/ldap_mod.c index 9761279fc6c..8d058aff530 100644 --- a/modules/ldap/ldap_mod.c +++ b/modules/ldap/ldap_mod.c @@ -84,6 +84,7 @@ static int w_ldap_result_check_2(struct sip_msg* msg, * Default module parameter values */ #define DEF_LDAP_CONFIG "/usr/local/etc/opensips/ldap.cfg" +#define DEF_REQ_CERT "NEVER" /* * Module parameter variables @@ -128,7 +129,7 @@ static cmd_export_t cmds[] = { */ static param_export_t params[] = { - {"config_file", STR_PARAM, &ldap_config}, + {"config_file", STR_PARAM, &ldap_config.s}, {0, 0, 0} };