/*****************************************************************************/ #ifdef COMMENTS_WITH_COMMENTS /* (wu)script.c This is strictly for a CGI and NOT a CGIplus environment. COPYRIGHT --------- Copyright (C) 2020,2021 Mark G.Daniel This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. http://www.gnu.org/licenses/gpl.txt VERSION HISTORY --------------- 03-AUG-2020 MGD ScriptCheckChallenge() supply host name as parameter 14-JUN-2020 MGD ScriptBegin() refine/relax script activation 13-DEC-2019 MGD initial */ #endif /* COMMENTS_WITH_COMMENTS */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wucme.h" #include "wucertman.h" #include "wureport.h" #define FI_LI "SCRIPT", __LINE__ extern char *argv0; extern char SoftwareId[]; extern char UacmeVersion[]; /*****************************************************************************/ /* Executing as a CGI script. */ void ScriptBegin (int argc, char* argv[]) { char *cptr, *sptr; FILE *stderr2; /*********/ /* begin */ /*********/ /* do not allow the CRTL to interfere with carriage-control */ if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin"))) EXIT_FI_LI (vaxc$errno); if (!(stderr = freopen ("SYS$OUTPUT:", "w", stderr, "ctx=bin"))) EXIT_FI_LI (vaxc$errno); UtilAdjustPriv(); stderr2 = stderr; stderr = stdout; /* advice to WATCHing sysadmin */ ScriptCallout ("!NOOP: %s (%s) (%s) %s\n", SoftwareId, UacmeVersion, OpenSSL_version(OPENSSL_VERSION), argv0); if (!(cptr = getenv ("WWW_REQUEST_METHOD"))) if (!(cptr = getenv ("REQUEST_METHOD"))) cptr = ""; if (!strcmp (cptr, "GET")) { if (!(sptr = getenv ("WWW_REQUEST_URI"))) if (!(sptr = getenv ("REQUEST_URI"))) sptr = ""; if (!(cptr = strstr (sptr, "/admin/"))) if (!(cptr = strstr (sptr, "/.well-known/acme-challenge/"))) cptr = ""; } if (!strncmp (cptr, "/admin/", 7)) ScriptAdmin (cptr+7); else if (!strncmp (cptr, "/.well-known/acme-challenge/", 28)) ScriptChallenge (cptr+28); else ScriptZero2C (); stderr = stderr2; exit (SS$_NORMAL); } /*****************************************************************************/ /* HTTP request for an administration URI. Must be subject to authorisation. The test functions here perform the activity under the scripting account (and of course using the INSTALLed image). */ void ScriptAdmin (char *what) { int argsc, status, target; char *cptr; #define MAX_ARGSV 32 char *argsv [MAX_ARGSV]; /*********/ /* begin */ /*********/ /* admin functions must be subject to authorisation */ if (!(cptr = getenv ("WWW_REMOTE_USER"))) if (!(cptr = getenv ("REMOTE_USER"))) cptr = ""; if (!*cptr) { /* advice to WATCHing sysadmin */ ScriptCallout ("!NOOP: wuCME /admin/ MUST BE AUTHORISED\n"); ScriptZero2C (); exit (SS$_NORMAL); } if (!strncmp (what, "cert", 4) || !strcmp (what, "list")) CertManAdminCert (); else if (!strcmp (what, "log")) { ScriptStreamHeader (); wucmeCheckLog (); } else if (!strncmp (what, "check/", 6)) { /* rest just for development/debugging purposes */ what += 5; if (!strcmp (what, "/ca")) { ScriptRecordHeader (); memset (argsv, argsc = 0, sizeof(argsv)); argsv[argsc++] = argv0; argsv[argsc++] = "--uacme"; if (UtilSysTrnLnm (WUCME_VERBOSE)) argsv[argsc++] = "--verbose"; argsv[argsc++] = "ping"; mainline (argsc, argsv); } else if (!strcmp (what, "/certman")) { ScriptRecordHeader (); CertManBegin (); } else if (!strcmp (what, "/challenge") || !strncmp (what, "/challenge?", 11)) { ScriptRecordHeader (); ScriptCheckChallenge (); } else if (!strcmp (what, "/load")) { ScriptRecordHeader (); CertManLoad (); } else if (!strcmp (what, "/log")) { ScriptStreamHeader (); wucmeCheckLog (); } else if (!strcmp (what, "/mail")) { ScriptStreamHeader (); if (cptr = UtilSysTrnLnm (WUCME_MAIL)) { cptr = strdup(cptr); fprintf (stdout, "Test of MAIL report: %s\n", cptr); ReportMail (MAIL_PERSONAL, cptr, "wuCME test only!", "wuCME test of MAIL report..."); } else { ScriptStreamHeader (); fprintf (stdout, "WUCME_MAIL not defined!\n"); } } else if (!strcmp (what, "/opcom")) { ScriptStreamHeader (); if (cptr = UtilSysTrnLnm (WUCME_OPCOM)) { cptr = strdup(cptr); target = ReportOpcomTargetOf(cptr); } else target = ReportOpcomTargetOf(cptr = "CENTRAL"); fprintf (stdout, "Test of OPCOM report: %s\n", cptr); ReportOpcom (target, "wuCME test of OPCOM report..."); } else { ScriptZero2C (); exit (SS$_NORMAL); } } else { ScriptZero2C (); exit (SS$_NORMAL); } fprintf (stdout, "%s: ^Z\n", tstamp(NULL)); exit (SS$_NORMAL); } /*****************************************************************************/ /* Respond to the /.well-known/acme-challenge/ request. */ void ScriptChallenge (char *token) { char *key; /*********/ /* begin */ /*********/ key = wucmeChallenge (token, NULL); if (key) fprintf (stdout, "Status: 200 OK\r\n\ Content-Length: %d\r\n\ Cache-Control: no-cache, no-store, must-revalidate\r\n\ Pragma: no-cache\r\n\ Expires: 0\r\n\ \r\n\ %s", strlen(key), key); else { ScriptStreamHeader (); fprintf (stdout, "Challenge received but no such token.\n"); } } /*****************************************************************************/ /* Using wuCME's internal HTTP(S) client, poll the ACME-challenge URI on port 80, first checking for a server-configured service, then if that is unavailable, deploying the internal challenge responder and probing that. Provide informational output for the user. See wumceChallenge80(). */ void ScriptCheckChallenge (void) { int rcode; char *cptr, *qptr; /*********/ /* begin */ /*********/ if (UtilSysTrnLnm (WUCME_VERBOSE)) verboserer(1); if (!(cptr = qptr = getenv("WWW_QUERY_STRING"))) cptr = qptr = getenv("QUERY_STRING"); /* query string can be empty */ if (!cptr || !*cptr) if (!(cptr = getenv("WWW_SERVER_NAME"))) if (!(cptr = getenv("SERVER_NAME"))) cptr = "localhost"; warnx ("TRY %s standard port 80", cptr); rcode = wucmeProbe80 ((const char*)cptr, 5); if (rcode <= 0) { warnx ("FAILED to connect to port 80 service"); /* if host name via query string internal responder not applicable */ if (!qptr || !*qptr) { warnx ("deploying internal port 80 responder"); Http01Spawn (1); rcode = wucmeProbe80 ((const char*)cptr, 10); if (rcode <= 0) warnx ("FAILED to connect to internal responder"); else if (rcode == 200) warnx ("SUCCEEDED via internal responder"); else warnx ("FAILED (%d) via internal responder", rcode); Http01Spawn (0); } } else if (rcode == 200) warnx ("SUCCEEDED via port 80 service"); else warnx ("FAILED (%d) via port 80 service", rcode); verboserer(0); } /*****************************************************************************/ /* */ void ScriptZero2C (void) { char zero2c [] = "Status: 403\r\n\ Content-Type: text/plain\r\n\ Script-Control: X-stream-mode\r\n\ Cache-Control: no-cache, no-store, must-revalidate\r\n\ Pragma: no-cache\r\n\ Expires: 0\r\n\ \r\n\ Nothing to see here ... move along.\n"; /*********/ /* begin */ /*********/ fputs (zero2c, stdout); fflush (stdout); } /*****************************************************************************/ /* */ void ScriptRecordHeader (void) { char record200 [] = "Status: 200\r\n\ Content-Type: text/plain\r\n\ Script-Control: X-record-mode\r\n\ Cache-Control: no-cache, no-store, must-revalidate\r\n\ Pragma: no-cache\r\n\ Expires: 0\r\n\ \r\n"; /*********/ /* begin */ /*********/ fputs (record200, stdout); fflush (stdout); } /*****************************************************************************/ /* */ void ScriptStreamHeader (void) { char stream200 [] = "Status: 200\r\n\ Content-Type: text/plain\r\n\ Script-Control: X-stream-mode\r\n\ Cache-Control: no-cache, no-store, must-revalidate\r\n\ Pragma: no-cache\r\n\ Expires: 0\r\n\ \r\n"; /*********/ /* begin */ /*********/ fputs (stream200, stdout); fflush (stdout); } /*****************************************************************************/ /* Provide a callout to WASD server. */ void ScriptCallout (char *fmt, ...) { int retval; char *cptr; va_list ap; /* must be received as records */ fflush (stdout); fprintf (stdout, "%s\n", getenv("CGIPLUSESC")); fflush (stdout); va_start (ap, fmt); retval = vasprintf (&cptr, fmt, ap); va_end (ap); if (retval >= 0) { fputs (cptr, stdout); free (cptr); } else fprintf (stdout, "vasprintf() %d", vaxc$errno); fflush (stdout); fprintf (stdout, "%s\n", getenv("CGIPLUSEOT")); fflush (stdout); } /*****************************************************************************/