diff --git a/include/common/defaults.h b/include/common/defaults.h index b63870e65..0653a5310 100644 --- a/include/common/defaults.h +++ b/include/common/defaults.h @@ -177,7 +177,6 @@ #define DEF_AGENT_RISETIME 1 #define DEF_CHECK_REQ "OPTIONS / HTTP/1.0\r\n" #define DEF_CHECK_PATH "" -#define DEF_LDAP_CHECK_REQ "\x30\x0c\x02\x01\x01\x60\x07\x02\x01\x03\x04\x00\x80\x00" #define DEF_HANA_ONERR HANA_ONERR_FAILCHK diff --git a/include/proto/checks.h b/include/proto/checks.h index 322f9ddf3..ff432384a 100644 --- a/include/proto/checks.h +++ b/include/proto/checks.h @@ -79,6 +79,8 @@ int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, s const char *file, int line); int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, const char *file, int line); +int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line); #endif /* _PROTO_CHECKS_H */ diff --git a/include/types/checks.h b/include/types/checks.h index 5b3fda4ad..a5676c18a 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -316,6 +316,7 @@ struct tcpcheck_rule { #define TCPCHK_RULES_REDIS_CHK 0x00000020 #define TCPCHK_RULES_SMTP_CHK 0x00000030 #define TCPCHK_RULES_MYSQL_CHK 0x00000050 +#define TCPCHK_RULES_LDAP_CHK 0x00000060 #define TCPCHK_RULES_SSL3_CHK 0x00000070 /* A list of tcp-check vars, to be registered before executing a ruleset */ diff --git a/include/types/proxy.h b/include/types/proxy.h index 6e8403737..bf2a798b3 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -171,9 +171,7 @@ enum PR_SRV_STATE_FILE { #define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */ /* unused: 0x10000000..0x30000000 */ #define PR_O2_HTTP_CHK 0x40000000 /* use HTTP 'OPTIONS' method to check server health */ -/* unused 0x50000000 */ -#define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */ -/* unused: 0x70000000 */ +/* unused 0x50000000..0x70000000 */ #define PR_O2_LB_AGENT_CHK 0x80000000 /* use a TCP connection to obtain a metric of server health */ #define PR_O2_TCPCHK_CHK 0x90000000 /* use TCPCHK check for server health */ #define PR_O2_EXT_CHK 0xA0000000 /* use external command for server health */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index b731a2911..428674258 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -2422,16 +2422,8 @@ stats_error_parsing: goto out; } else if (!strcmp(args[1], "ldap-check")) { - /* use LDAP request to check servers' health */ - free(curproxy->check_req); - curproxy->check_req = NULL; - curproxy->options2 &= ~PR_O2_CHK_ANY; - curproxy->options2 |= PR_O2_LDAP_CHK; - - curproxy->check_req = malloc(sizeof(DEF_LDAP_CHECK_REQ) - 1); - memcpy(curproxy->check_req, DEF_LDAP_CHECK_REQ, sizeof(DEF_LDAP_CHECK_REQ) - 1); - curproxy->check_len = sizeof(DEF_LDAP_CHECK_REQ) - 1; - if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code)) + err_code |= proxy_parse_ldap_check_opt(args, 0, curproxy, &defproxy, file, linenum); + if (err_code & ERR_FATAL) goto out; } else if (!strcmp(args[1], "spop-check")) { diff --git a/src/checks.c b/src/checks.c index 0fcf91592..fa595ea65 100644 --- a/src/checks.c +++ b/src/checks.c @@ -857,7 +857,6 @@ static void __event_srv_chk_r(struct conn_stream *cs) struct task *t = check->task; char *desc; int done; - unsigned short msglen; if (unlikely(check->result == CHK_RES_FAILED)) goto out_wakeup; @@ -1156,59 +1155,6 @@ static void __event_srv_chk_r(struct conn_stream *cs) break; } - case PR_O2_LDAP_CHK: - if (!done && b_data(&check->bi) < 14) - goto wait_more_data; - - /* Check if the server speaks LDAP (ASN.1/BER) - * http://en.wikipedia.org/wiki/Basic_Encoding_Rules - * http://tools.ietf.org/html/rfc4511 - */ - - /* http://tools.ietf.org/html/rfc4511#section-4.1.1 - * LDAPMessage: 0x30: SEQUENCE - */ - if ((b_data(&check->bi) < 14) || (*(b_head(&check->bi)) != '\x30')) { - set_server_check_status(check, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol"); - } - else { - /* size of LDAPMessage */ - msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0; - - /* http://tools.ietf.org/html/rfc4511#section-4.2.2 - * messageID: 0x02 0x01 0x01: INTEGER 1 - * protocolOp: 0x61: bindResponse - */ - if ((msglen > 2) || - (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) { - set_server_check_status(check, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol"); - goto out_wakeup; - } - - /* size of bindResponse */ - msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0; - - /* http://tools.ietf.org/html/rfc4511#section-4.1.9 - * ldapResult: 0x0a 0x01: ENUMERATION - */ - if ((msglen > 4) || - (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) { - set_server_check_status(check, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol"); - goto out_wakeup; - } - - /* http://tools.ietf.org/html/rfc4511#section-4.1.9 - * resultCode - */ - check->code = *(b_head(&check->bi) + msglen + 9); - if (check->code) { - set_server_check_status(check, HCHK_STATUS_L7STS, "See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9"); - } else { - set_server_check_status(check, HCHK_STATUS_L7OKD, "Success"); - } - } - break; - case PR_O2_SPOP_CHK: { unsigned int framesz; char err[HCHK_DESC_LEN]; @@ -2770,6 +2716,71 @@ static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, stru return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read); } +static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read) +{ + enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE; + enum healthcheck_status status; + struct buffer *msg = NULL; + struct ist desc = ist(NULL); + unsigned short msglen = 0; + + /* Check if the server speaks LDAP (ASN.1/BER) + * http://en.wikipedia.org/wiki/Basic_Encoding_Rules + * http://tools.ietf.org/html/rfc4511 + */ + /* size of LDAPMessage */ + msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0; + + /* http://tools.ietf.org/html/rfc4511#section-4.2.2 + * messageID: 0x02 0x01 0x01: INTEGER 1 + * protocolOp: 0x61: bindResponse + */ + if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) { + status = HCHK_STATUS_L7RSP; + desc = ist("Not LDAPv3 protocol"); + goto error; + } + + /* size of bindResponse */ + msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0; + + /* http://tools.ietf.org/html/rfc4511#section-4.1.9 + * ldapResult: 0x0a 0x01: ENUMERATION + */ + if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) { + status = HCHK_STATUS_L7RSP; + desc = ist("Not LDAPv3 protocol"); + goto error; + } + + /* http://tools.ietf.org/html/rfc4511#section-4.1.9 + * resultCode + */ + check->code = *(b_head(&check->bi) + msglen + 9); + if (check->code) { + status = HCHK_STATUS_L7STS; + desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9"); + goto error; + } + + set_server_check_status(check, HCHK_STATUS_L7OKD, "Success"); + + out: + free_trash_chunk(msg); + return ret; + + error: + ret = TCPCHK_EVAL_STOP; + msg = alloc_trash_chunk(); + if (msg) + tcpcheck_onerror_message(msg, check, rule, 0, desc); + set_server_check_status(check, status, (msg ? b_head(msg) : NULL)); + goto out; + + wait_more_data: + ret = TCPCHK_EVAL_WAIT; + goto out; +} /* Evaluate a TCPCHK_ACT_CONNECT rule. It returns 1 to evaluate the next rule, 0 * to wait and -1 to stop the check. */ @@ -5763,6 +5774,93 @@ int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, s goto out; } +int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line) +{ + static char *ldap_req = "300C020101600702010304008000"; + + struct tcpcheck_ruleset *rs = NULL; + struct tcpcheck_rules *rules = &curpx->tcpcheck_rules; + struct tcpcheck_rule *chk; + char *errmsg = NULL; + int err_code = 0; + + if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL)) + err_code |= ERR_WARN; + + if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code)) + goto out; + + if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) { + ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n", + file, line); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + curpx->options2 &= ~PR_O2_CHK_ANY; + curpx->options2 |= PR_O2_TCPCHK_CHK; + + free_tcpcheck_vars(&rules->preset_vars); + rules->list = NULL; + rules->flags = 0; + + rs = tcpcheck_ruleset_lookup("*ldap-check"); + if (rs) + goto ruleset_found; + + rs = tcpcheck_ruleset_create("*ldap-check"); + if (rs == NULL) { + ha_alert("parsing [%s:%d] : out of memory.\n", file, line); + goto error; + } + + chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 0; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30", + "min-recv", "14", + "on-error", "Not LDAPv3 protocol", + ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 1; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->expect.custom = tcpcheck_ldap_expect_bindrsp; + chk->index = 2; + LIST_ADDQ(&rs->rules, &chk->list); + + LIST_ADDQ(&tcpchecks_list, &rs->list); + + ruleset_found: + rules->list = &rs->rules; + rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_LDAP_CHK); + + out: + free(errmsg); + return err_code; + + error: + tcpcheck_ruleset_release(rs); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; +} static struct cfg_kw_list cfg_kws = {ILH, { { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },