"Fossies" - the Fresh Open Source Software Archive

Member "xymon-4.3.30/xymonnet/ldaptest.c" (23 Jul 2019, 16726 Bytes) of package /linux/privat/xymon-4.3.30.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ldaptest.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.3.29_vs_4.3.30.

    1 /*----------------------------------------------------------------------------*/
    2 /* Xymon monitor network test tool.                                           */
    3 /*                                                                            */
    4 /* This is used to implement the testing of an LDAP service.                  */
    5 /*                                                                            */
    6 /* Copyright (C) 2003-2011 Henrik Storner <henrik@hswn.dk>                    */
    7 /*                                                                            */
    8 /* This program is released under the GNU General Public License (GPL),       */
    9 /* version 2. See the file "COPYING" for details.                             */
   10 /*                                                                            */
   11 /*----------------------------------------------------------------------------*/
   12 
   13 static char rcsid[] = "$Id: ldaptest.c 8069 2019-07-23 15:29:06Z jccleaver $";
   14 
   15 #include <sys/types.h>
   16 #include <stdlib.h>
   17 #include <stdio.h>
   18 #include <stdarg.h>
   19 #include <string.h>
   20 #include <ctype.h>
   21 #include <unistd.h>
   22 #include <signal.h>
   23 
   24 #include "libxymon.h"
   25 
   26 #include "xymonnet.h"
   27 #include "ldaptest.h"
   28 
   29 #define XYMON_LDAP_OK       0
   30 #define XYMON_LDAP_INITFAIL 10
   31 #define XYMON_LDAP_TLSFAIL  11
   32 #define XYMON_LDAP_BINDFAIL 20
   33 #define XYMON_LDAP_TIMEOUT  30
   34 #define XYMON_LDAP_SEARCHFAILED 40
   35 
   36 char *ldap_library_version = NULL;
   37 
   38 static volatile int connect_timeout = 0;
   39 
   40 int init_ldap_library(void)
   41 {
   42 #ifdef HAVE_LDAP
   43     char versionstring[100];
   44 
   45     /* Doesnt really do anything except define the version-number string */
   46     snprintf(versionstring, sizeof(versionstring), "%s %d", LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
   47     ldap_library_version = strdup(versionstring);
   48 #endif
   49 
   50     return 0;
   51 }
   52 
   53 void shutdown_ldap_library(void)
   54 {
   55 #ifdef HAVE_LDAP
   56     /* No-op for LDAP */
   57 #endif
   58 }
   59 
   60 int add_ldap_test(testitem_t *t)
   61 {
   62 #ifdef HAVE_LDAP
   63     testitem_t *basecheck;
   64     ldap_data_t *req;
   65     LDAPURLDesc *ludp;
   66     char *urltotest;
   67     int badurl;
   68 
   69     basecheck = (testitem_t *)t->privdata;
   70 
   71     /* 
   72      * t->testspec containts the full testspec
   73      * We need to remove any URL-encoding.
   74      */
   75     urltotest = urlunescape(t->testspec);
   76     badurl = (ldap_url_parse(urltotest, &ludp) != 0);
   77 
   78     /* Allocate the private data and initialize it */
   79     t->privdata = (void *) calloc(1, sizeof(ldap_data_t)); 
   80     req = (ldap_data_t *) t->privdata;
   81     req->ldapdesc = (void *) ludp;
   82     req->usetls = (strncmp(urltotest, "ldaps:", 6) == 0);
   83     if (req->usetls && (ludp->lud_port == LDAPS_PORT)) {
   84         dbgprintf("Forcing port %d for ldaps with STARTTLS\n", LDAP_PORT );
   85         ludp->lud_port = LDAP_PORT;
   86     }
   87     req->ldapstatus = 0;
   88     req->output = NULL;
   89     req->ldapcolor = -1;
   90     req->faileddeps = NULL;
   91     req->duration.tv_sec = req->duration.tv_nsec = 0;
   92     req->certinfo = NULL;
   93     req->certissuer = NULL;
   94     req->certexpires = 0;
   95     req->certkeysz = 0;
   96     req->skiptest = 0;
   97 
   98     if (badurl) {
   99         errprintf("Invalid LDAP URL %s\n", t->testspec);
  100         req->skiptest = 1;
  101         req->ldapstatus = XYMON_LDAP_BINDFAIL;
  102         req->output = "Cannot parse LDAP URL";
  103     }
  104 
  105     /*
  106      * At this point, the plain TCP checks have already run.
  107      * So we know from the test found in t->privdata whether
  108      * the LDAP port is open.
  109      * If it is not open, then don't run this check.
  110      */
  111     if (basecheck->open == 0) {
  112         /* Cannot connect to LDAP port. */
  113         req->skiptest = 1;
  114         req->ldapstatus = XYMON_LDAP_BINDFAIL;
  115         req->output = "Cannot connect to server";
  116     }
  117 
  118 #endif
  119 
  120     return 0;
  121 }
  122 
  123 
  124 #if (LDAP_VENDOR != OpenLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
  125 static void ldap_alarmhandler(int signum)
  126 {
  127     signal(signum, SIG_DFL);
  128     connect_timeout = 1;
  129 }
  130 #endif
  131 
  132 void run_ldap_tests(service_t *ldaptest, int sslcertcheck, int querytimeout)
  133 {
  134 #ifdef HAVE_LDAP
  135     ldap_data_t *req;
  136     testitem_t *t;
  137     struct timespec starttime;
  138     struct timespec endtime;
  139 
  140     /* Pick a sensible default for the timeout setting */
  141     if (querytimeout == 0) querytimeout = 30;
  142 
  143     for (t = ldaptest->items; (t); t = t->next) {
  144         LDAPURLDesc *ludp;
  145         LDAP        *ld;
  146         int     rc, finished;
  147         int     msgID = -1;
  148         struct timeval  ldaptimeout;
  149         struct timeval  openldaptimeout;
  150         LDAPMessage *result;
  151         LDAPMessage *e;
  152         strbuffer_t *response;
  153         char        buf[MAX_LINE_LEN];
  154 
  155         req = (ldap_data_t *) t->privdata;
  156         if (req->skiptest) continue;
  157 
  158         ludp = (LDAPURLDesc *) req->ldapdesc;
  159 
  160         getntimer(&starttime);
  161 
  162         /* Initiate session with the LDAP server */
  163         dbgprintf("Initiating LDAP session for host %s port %d\n",
  164             ludp->lud_host, ludp->lud_port);
  165 
  166         if( (ld = ldap_init(ludp->lud_host, ludp->lud_port)) == NULL ) {
  167             dbgprintf("ldap_init failed\n");
  168             req->ldapstatus = XYMON_LDAP_INITFAIL;
  169             continue;
  170         }
  171 
  172         /* 
  173          * There is apparently no standard way of defining a network
  174          * timeout for the initial connection setup. 
  175          */
  176 #if (LDAP_VENDOR == OpenLDAP) && defined(LDAP_OPT_NETWORK_TIMEOUT)
  177         /* 
  178          * OpenLDAP has an undocumented ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv)
  179          */
  180         openldaptimeout.tv_sec = querytimeout;
  181         openldaptimeout.tv_usec = 0;
  182         ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &openldaptimeout);
  183 #else
  184         /*
  185          * So using an alarm() to interrupt any pending operations
  186          * seems to be the least insane way of doing this.
  187          *
  188          * Note that we must do this right after ldap_init(), as
  189          * any operation on the session handle (ld) may trigger the
  190          * network connection to be established.
  191          */
  192         connect_timeout = 0;
  193         signal(SIGALRM, ldap_alarmhandler);
  194         alarm(querytimeout);
  195 #endif
  196 
  197         /*
  198          * This is completely undocumented in the OpenLDAP docs.
  199          * But apparently it is documented in 
  200          * http://www.ietf.org/proceedings/99jul/I-D/draft-ietf-ldapext-ldap-c-api-03.txt
  201          *
  202          * Both of these routines appear in the <ldap.h> file 
  203          * from OpenLDAP 2.1.22. Their use to enable TLS has
  204          * been deciphered from the ldapsearch() utility
  205          * sourcecode.
  206          *
  207          * According to Manon Goo <manon@manon.de>, recent (Jan. 2005)
  208          * OpenLDAP implementations refuse to talk LDAPv2.
  209          */
  210 #ifdef LDAP_OPT_PROTOCOL_VERSION 
  211         {
  212             int protocol = LDAP_VERSION3;
  213 
  214             dbgprintf("Attempting to select LDAPv3\n");
  215             if ((rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) {
  216                 dbgprintf("Failed to select LDAPv3, trying LDAPv2\n");
  217                 protocol = LDAP_VERSION2;
  218                 if ((rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) {
  219                     req->output = strdup(ldap_err2string(rc));
  220                     req->ldapstatus = XYMON_LDAP_TLSFAIL;
  221                 }
  222                 continue;
  223             }
  224         }
  225 #endif
  226 
  227         if (req->usetls) {
  228             dbgprintf("Trying to enable TLS for session\n");
  229             if ((rc = ldap_start_tls_s(ld, NULL, NULL)) != LDAP_SUCCESS) {
  230                 dbgprintf("ldap_start_tls failed\n");
  231                 req->output = strdup(ldap_err2string(rc));
  232                 req->ldapstatus = XYMON_LDAP_TLSFAIL;
  233                 continue;
  234             }
  235         }
  236 
  237         if (!connect_timeout) {
  238             msgID = ldap_simple_bind(ld, (t->host->ldapuser ? t->host->ldapuser : ""), 
  239                      (t->host->ldappasswd ? t->host->ldappasswd : ""));
  240         }
  241 
  242         /* Cancel any pending alarms */
  243         alarm(0);
  244         signal(SIGALRM, SIG_DFL);
  245 
  246         /* Did we connect? */
  247         if (connect_timeout || (msgID == -1)) {
  248             req->ldapstatus = XYMON_LDAP_BINDFAIL;
  249             req->output = "Cannot connect to server";
  250             continue;
  251         }
  252 
  253         /* Wait for bind to complete */
  254         rc = 0; finished = 0; 
  255         ldaptimeout.tv_sec = querytimeout;
  256         ldaptimeout.tv_usec = 0L;
  257         while( ! finished ) {
  258             int rc2;
  259 
  260             rc = ldap_result(ld, msgID, LDAP_MSG_ONE, &ldaptimeout, &result);
  261             dbgprintf("ldap_result returned %d for ldap_simple_bind()\n", rc);
  262             if(rc == -1) {
  263                 finished = 1;
  264                 req->ldapstatus = XYMON_LDAP_BINDFAIL;
  265 
  266                 if (result == NULL) {
  267                     errprintf("LDAP library problem - NULL result returned\n");
  268                     req->output = strdup("LDAP BIND failed\n");
  269                 }
  270                 else {
  271                     rc2 = ldap_result2error(ld, result, 1);
  272                     req->output = strdup(ldap_err2string(rc2));
  273                 }
  274                 ldap_unbind(ld);
  275             }
  276             else if (rc == 0) {
  277                 finished = 1;
  278                 req->ldapstatus = XYMON_LDAP_BINDFAIL;
  279                 req->output = strdup("Connection timeout");
  280                 ldap_unbind(ld);
  281             }
  282             else if( rc > 0 ) {
  283                 finished = 1;
  284                 if (result == NULL) {
  285                     errprintf("LDAP library problem - got a NULL resultcode for status %d\n", rc);
  286                     req->ldapstatus = XYMON_LDAP_BINDFAIL;
  287                     req->output = strdup("LDAP library problem: ldap_result2error returned a NULL result for status %d\n");
  288                     ldap_unbind(ld);
  289                 }
  290                 else {
  291                     rc2 = ldap_result2error(ld, result, 1);
  292                     if(rc2 != LDAP_SUCCESS) {
  293                         req->ldapstatus = XYMON_LDAP_BINDFAIL;
  294                         req->output = strdup(ldap_err2string(rc));
  295                         ldap_unbind(ld);
  296                     }
  297                 }
  298             }
  299         } /* ... while() */
  300 
  301         /* We're done connecting. If something went wrong, go to next query. */
  302         if (req->ldapstatus != 0) continue;
  303 
  304         /* Now do the search. With a timeout */
  305         ldaptimeout.tv_sec = querytimeout;
  306         ldaptimeout.tv_usec = 0L;
  307         rc = ldap_search_st(ld, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter, ludp->lud_attrs, 0, &ldaptimeout, &result);
  308 
  309         if(rc == LDAP_TIMEOUT) {
  310             req->ldapstatus = XYMON_LDAP_TIMEOUT;
  311             req->output = strdup(ldap_err2string(rc));
  312             ldap_unbind(ld);
  313             continue;
  314         }
  315         if( rc != LDAP_SUCCESS ) {
  316             req->ldapstatus = XYMON_LDAP_SEARCHFAILED;
  317             req->output = strdup(ldap_err2string(rc));
  318             ldap_unbind(ld);
  319             continue;
  320         }
  321 
  322         getntimer(&endtime);
  323 
  324         response = newstrbuffer(0);
  325         snprintf(buf, sizeof(buf), "Searching LDAP for %s yields %d results:\n\n", 
  326             t->testspec, ldap_count_entries(ld, result));
  327         addtobuffer(response, buf);
  328 
  329         for(e = ldap_first_entry(ld, result); (e != NULL); e = ldap_next_entry(ld, e) ) {
  330             char        *dn;
  331             BerElement  *ber;
  332             char        *attribute;
  333             char        **vals;
  334 
  335             dn = ldap_get_dn(ld, e);
  336             snprintf(buf, sizeof(buf), "DN: %s\n", dn); 
  337             addtobuffer(response, buf);
  338 
  339             /* Addtributes and values */
  340             for (attribute = ldap_first_attribute(ld, e, &ber); (attribute != NULL); attribute = ldap_next_attribute(ld, e, ber) ) {
  341                 if ((vals = ldap_get_values(ld, e, attribute)) != NULL) {
  342                     int i;
  343 
  344                     for(i = 0; (vals[i] != NULL); i++) {
  345                         snprintf(buf, sizeof(buf), "\t%s: %s\n", attribute, vals[i]);
  346                         addtobuffer(response, buf);
  347                     }
  348                 }
  349                 /* Free memory used to store values */
  350                 ldap_value_free(vals);
  351             }
  352 
  353             /* Free memory used to store attribute */
  354             ldap_memfree(attribute);
  355             ldap_memfree(dn);
  356             if (ber != NULL) ber_free(ber, 0);
  357 
  358             addtobuffer(response, "\n");
  359         }
  360         req->ldapstatus = XYMON_LDAP_OK;
  361         req->output = grabstrbuffer(response);
  362         tvdiff(&starttime, &endtime, &req->duration);
  363 
  364         ldap_msgfree(result);
  365         ldap_unbind(ld);
  366         ldap_free_urldesc(ludp);
  367     }
  368 #endif
  369 }
  370 
  371 
  372 static int statuscolor(testedhost_t *host, int ldapstatus)
  373 {
  374     switch (ldapstatus) {
  375       case XYMON_LDAP_OK:
  376         return COL_GREEN;
  377 
  378       case XYMON_LDAP_INITFAIL:
  379       case XYMON_LDAP_TLSFAIL:
  380       case XYMON_LDAP_BINDFAIL:
  381       case XYMON_LDAP_TIMEOUT:
  382         return COL_RED;
  383 
  384       case XYMON_LDAP_SEARCHFAILED:
  385         return (host->ldapsearchfailyellow ? COL_YELLOW : COL_RED);
  386     }
  387 
  388     errprintf("Unknown ldapstaus value %d\n", ldapstatus);
  389     return COL_RED;
  390 }
  391 
  392 void send_ldap_results(service_t *ldaptest, testedhost_t *host, char *nonetpage, int failgoesclear)
  393 {
  394     testitem_t *t;
  395     int color = -1;
  396     char    msgline[4096];
  397     SBUF_DEFINE(nopagename);
  398     int     nopage = 0;
  399     testitem_t *ldap1 = host->firstldap;
  400     int anydown = 0;
  401     char    *svcname;
  402 
  403     if (ldap1 == NULL) return;
  404 
  405     svcname = strdup(ldaptest->testname);
  406     if (ldaptest->namelen) svcname[ldaptest->namelen] = '\0';
  407 
  408     /* Check if this service is a NOPAGENET service. */
  409     SBUF_MALLOC(nopagename, strlen(svcname)+3);
  410     snprintf(nopagename, nopagename_buflen, ",%s,", svcname);
  411     nopage = (strstr(nonetpage, nopagename) != NULL);
  412     xfree(nopagename);
  413 
  414     dbgprintf("Calc ldap color host %s : ", host->hostname);
  415     for (t=host->firstldap; (t && (t->host == host)); t = t->next) {
  416         ldap_data_t *req = (ldap_data_t *) t->privdata;
  417 
  418         req->ldapcolor = statuscolor(host, req->ldapstatus);
  419         if (req->ldapcolor == COL_RED) anydown++;
  420 
  421         /* Dialup hosts and dialup tests report red as clear */
  422         if ((req->ldapcolor != COL_GREEN) && (host->dialup || t->dialup)) req->ldapcolor = COL_CLEAR;
  423 
  424         /* If ping failed, report CLEAR unless alwaystrue */
  425         if ( ((req->ldapcolor == COL_RED) || (req->ldapcolor == COL_YELLOW)) && /* Test failed */
  426              (host->downcount > 0)                   && /* The ping check did fail */
  427              (!host->noping && !host->noconn)        && /* We are doing a ping test */
  428              (failgoesclear)                         &&
  429              (!t->alwaystrue)                           )  /* No "~testname" flag */ {
  430             req->ldapcolor = COL_CLEAR;
  431         }
  432 
  433         /* If test we depend on has failed, report CLEAR unless alwaystrue */
  434         if ( ((req->ldapcolor == COL_RED) || (req->ldapcolor == COL_YELLOW)) && /* Test failed */
  435               failgoesclear && !t->alwaystrue )  /* No "~testname" flag */ {
  436             char *faileddeps = deptest_failed(host, t->service->testname);
  437 
  438             if (faileddeps) {
  439                 req->ldapcolor = COL_CLEAR;
  440                 req->faileddeps = strdup(faileddeps);
  441             }
  442         }
  443 
  444         dbgprintf("%s(%s) ", t->testspec, colorname(req->ldapcolor));
  445         if (req->ldapcolor > color) color = req->ldapcolor;
  446     }
  447 
  448     if (anydown) ldap1->downcount++; else ldap1->downcount = 0;
  449 
  450     /* Handle the "badtest" stuff for ldap tests */
  451     if ((color == COL_RED) && (ldap1->downcount < ldap1->badtest[2])) {
  452         if      (ldap1->downcount >= ldap1->badtest[1]) color = COL_YELLOW;
  453         else if (ldap1->downcount >= ldap1->badtest[0]) color = COL_CLEAR;
  454         else                                            color = COL_GREEN;
  455     }
  456 
  457     if (nopage && (color == COL_RED)) color = COL_YELLOW;
  458     dbgprintf(" --> %s\n", colorname(color));
  459 
  460     /* Send off the ldap status report */
  461     init_status(color);
  462     snprintf(msgline, sizeof(msgline), "status+%d %s.%s %s %s", 
  463         validity, commafy(host->hostname), svcname, colorname(color), timestamp);
  464     addtostatus(msgline);
  465 
  466     for (t=host->firstldap; (t && (t->host == host)); t = t->next) {
  467         ldap_data_t *req = (ldap_data_t *) t->privdata;
  468 
  469         snprintf(msgline, sizeof(msgline), "\n&%s %s - %s\n\n", colorname(req->ldapcolor), t->testspec,
  470             ((req->ldapcolor != COL_GREEN) ? "failed" : "OK"));
  471         addtostatus(msgline);
  472 
  473         if (req->output) {
  474             addtostatus(req->output);
  475             addtostatus("\n\n");
  476         }
  477         if (req->faileddeps) addtostatus(req->faileddeps);
  478 
  479         snprintf(msgline, sizeof(msgline), "\nSeconds: %u.%.9ld\n",
  480             (unsigned int)req->duration.tv_sec, req->duration.tv_nsec);
  481 
  482         addtostatus(msgline);
  483     }
  484     addtostatus("\n");
  485     finish_status();
  486     xfree(svcname);
  487 }
  488 
  489 
  490 void show_ldap_test_results(service_t *ldaptest)
  491 {
  492     ldap_data_t *req;
  493     testitem_t *t;
  494 
  495     for (t = ldaptest->items; (t); t = t->next) {
  496         req = (ldap_data_t *) t->privdata;
  497 
  498         printf("URL        : %s\n", t->testspec);
  499         printf("Time spent : %u.%.9ld\n", 
  500             (unsigned int)req->duration.tv_sec, 
  501             req->duration.tv_nsec);
  502         printf("LDAP output:\n%s\n", textornull(req->output));
  503         printf("------------------------------------------------------\n");
  504     }
  505 }
  506 
  507 
  508 #ifdef STANDALONE
  509 
  510 /* These are dummy vars needed by stuff in util.c */
  511 hostlist_t      *hosthead = NULL;
  512 link_t          *linkhead = NULL;
  513 link_t  null_link = { "", "", "", NULL };
  514 
  515 char *deptest_failed(testedhost_t *host, char *testname)
  516 {
  517     return NULL;
  518 }
  519 
  520 int main(int argc, char *argv[])
  521 {
  522     testitem_t item;
  523     testedhost_t host;
  524     service_t ldapservice;
  525     int argi = 1;
  526     int ldapdebug = 0;
  527 
  528     while ((argi < argc) && (strncmp(argv[argi], "--", 2) == 0)) {
  529         if (strcmp(argv[argi], "--debug") == 0) {
  530             debug = 1;
  531         }
  532         else if (strncmp(argv[argi], "--ldapdebug=", strlen("--ldapdebug=")) == 0) {
  533             char *p = strchr(argv[argi], '=');
  534             ldapdebug = atoi(p+1);
  535         }
  536         argi++;
  537     }
  538 
  539     /* For testing, don't crash in sendmsg when no XYMSRV defined */
  540     dontsendmessages = 1;
  541     if (xgetenv("XYMSRV") == NULL) putenv("XYMSRV=127.0.0.1");
  542 
  543     memset(&item, 0, sizeof(item));
  544     memset(&host, 0, sizeof(host));
  545     memset(&ldapservice, 0, sizeof(ldapservice));
  546 
  547     ldapservice.portnum = 389;
  548     ldapservice.testname = "ldap";
  549     ldapservice.namelen = strlen(ldapservice.testname);
  550     ldapservice.items = &item;
  551 
  552     item.host = &host;
  553     item.service = &ldapservice;
  554     item.dialup = item.reverse = item.silenttest = item.alwaystrue = 0;
  555     item.testspec = urlunescape(argv[argi]);
  556 
  557     host.firstldap = &item;
  558     host.hostname = "ldaptest.xymon";
  559     host.ldapuser = NULL;
  560     host.ldappasswd = NULL;
  561 
  562     init_ldap_library();
  563 
  564     if (ldapdebug) {
  565 #if defined(LBER_OPT_DEBUG_LEVEL) && defined(LDAP_OPT_DEBUG_LEVEL)
  566         ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldapdebug);
  567         ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &ldapdebug);
  568 #else
  569         printf("LDAP library does not support change of debug level\n");
  570 #endif
  571     }
  572 
  573     if (add_ldap_test(&item) == 0) {
  574         run_ldap_tests(&ldapservice, 0, 10);
  575         combo_start();
  576         send_ldap_results(&ldapservice, &host, "", 0);
  577         combo_end();
  578     }
  579 
  580     shutdown_ldap_library();
  581     return 0;
  582 }
  583 
  584 #endif
  585