From: "Steven Levine" Received: from [192.168.100.201] (HELO mail.2rosenthals.com) by 2rosenthals.com (CommuniGate Pro SMTP 5.4.10) with ESMTP id 11070661 for ecs-isp@2rosenthals.com; Mon, 07 Oct 2024 15:44:39 -0400 Received: from secmgr-va.randr ([192.168.200.201]:39090 helo=mail2.2rosenthals.com) by mail.2rosenthals.com with esmtp (Exim 4.97.1) (envelope-from ) id 1sxteg-0000000080K-0nuD for ecs-isp@2rosenthals.com; Mon, 07 Oct 2024 15:44:30 -0400 Received: from mta-202a.earthlink-vadesecure.net ([51.81.232.240]:33193) by mail2.2rosenthals.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.97.1) (envelope-from ) id 1sxteY-00000000275-04WB for ecs-isp@2rosenthals.com; Mon, 07 Oct 2024 15:44:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; bh=bwvYSpR1CAVXg0RHS8zhygYEBDjXDUqAMeo59d phHOM=; c=relaxed/relaxed; d=earthlink.net; h=from:reply-to:subject: date:to:cc:resent-date:resent-from:resent-to:resent-cc:in-reply-to: references:list-id:list-help:list-unsubscribe:list-unsubscribe-post: list-subscribe:list-post:list-owner:list-archive; q=dns/txt; s=dk12062016; t=1728330261; x=1728935061; b=nVdrLEQwjTUqpRTmvR3auwUpcPR CcyRWM2/mNnMwbTyLPdni+otD+HXNLzg+F884Sq4rIwXMzX37pVsXA5P4v1p1GWQh6Xym0z U+AxPMgnWLQ6yrbr62a0H9sayAJ9yHnF/C/tDoPHyiMnThzrWa7wppKYl6Gz75cRUG8R+e+ VGR25Tcz6TaHCM67q097wjaVFQCxthLcAAFjr/jgo43nXLC4R2/G8ueoSWsIvEu8h2LLNJi VuKNCpcHGY+TieTpss8csho0dXDtNqkQFMJGleqnCBrwZ1cIoS7csRV0iJf8LlytmjcwAEM kmPEOc8nd5XHEYmSoNE8V5OIAWyYruQ== Received: from slamain ([172.58.119.108]) by vsel2nmtao02p.internal.vadesecure.com with ngmta id 9928714f-17fc433709b1bacd; Mon, 07 Oct 2024 19:44:20 +0000 Message-ID: <67043d4f.22.mr2ice.fgrirsq@earthlink.net> Date: Mon, 07 Oct 2024 11:58:07 -0800 To: "eCS ISP Mailing List" In-Reply-To: Subject: Re: [eCS-ISP] HTTPS-Misery (for Steven) X-Mailer: MR/2 Internet Cruiser Edition for OS/2 v3.00.11.24/60 In , on 10/07/24 at 08:22 PM, "Massimo S." said: >i'm trying to catch that challenge value to verify when they accept >HTTP-01 to improve the script I have appended a copy of the uacme-hook.cmd that Dan and I use. The interesting code is above the /*==============================================================================*/ /*=== SkelRexxFunc standards - Delete unused - Move modified above this mark ===*/ /*==============================================================================*/ separator. Everything below is boilerplate code that is maintained by my tools. You may notice that the script is almost 100% generic. Since our mapping of domain names to document roots is consistent, I saw no need to parse the httpd conf files or read the mappings from an external file. The relevant mapping code follows the /* Map domain to VirtualHost docroot directory */ comment. To handle possible uacme.exe failures, we back up the certificates before the uacme.exe runs and configure httpd to use the certificates as created by uacme. Steven -- ---------------------------------------------------------------------- "Steven Levine" Warp/DIY/BlueLion etc. www.scoug.com www.arcanoae.com www.warpcave.com ---------------------------------------------------------------------- /* uacme-hook - uacme hook script Expects to be install where uacme.exe can find it Expects to be able to map the ident passed to hook to challenge directory name Ident is typically same as domain name Typical usage is uacme -v -h uacme-hook.cmd issue example.com Copyright (c) 2024 Steven Levine and Associates, Inc. All rights reserved. This program is free software licensed under the terms of the GNU General Public License Version 3 or newer. The GPL Software License can be found in gnugpl3.txt or at http://www.gnu.org/licenses/licenses.html#GPL 2024-08-12 SHL Baseline 2024-08-19 SHL More generic 2024-10-07 SHL Comments */ signal on Error signal on Failure name Error signal on Halt signal on NotReady name Error signal on NoValue name Error signal on Syntax name Error Globals = 'gVersion gCmdName gEnv gLogDir gLogFile gTesting gTokenFile', 'gMethod gType gIdent gToken gAuth' gVersion = '0.1 2024-10-07' call Initialize Main: parse arg gMethod gType gIdent gToken gAuth gAuth = strip(gAuth) /* Optionally override default log directory */ logdir = 'd:\logs' if IsDir(logdir) then gLogDir = logDir call LogWriteVTSC '',, gCmdName 'started at' MakeTimeStamp(),, 'method is' gMethod,, 'type is' gType,, 'ident is' gIdent,, 'token is' gToken,, 'auth is' gAuth /* FIXME to be gone somewhen */ gTesting = 0 if GetEnv('HOSTNAME') == 'slamain' then do call LogWriteVTSC '', 'Running on slamain in test mode' gTesting = 1 end /* Find docs directory for server */ docsdir = left(directory(), 1) || ':\www\docs\' if gTesting then docsdir = left(directory(), 1) || ':\Internet\apache24-data\htdocs' if \ IsDir(docsdir) then call Die 'Cannot access' docsdir 'directory' /* Map domain to VirtualHost docroot directory */ ndx = lastpos('.', gIdent) if ndx = 0 then do domain = gIdent suffix = '' end else do suffix = substr(gIdent, ndx + 1) /* Without dot */ domain = left(gIdent, ndx - 1) ndx = lastpos('.', domain) if ndx > 0 then domain = substr(domain, ndx + 1) end docroot = MakePath( docsdir, domain) if \ IsDir(docroot) then docroot = MakePath( docsdir, domain || suffix) /* cih.bz -> cihbz */ if \ IsDir(docroot) then call Die 'Cannot access' docroot 'directory for' gIdent challengeDir = MakePath( docroot, '.well-known\acme-challenge') if \ IsDir(challengeDir) then call Die 'Cannot access' challengeDir 'directory' gTokenFile = MakePath(challengeDir, gToken) select when gMethod == 'begin' then call DoBegin when gMethod == 'done' then call DoDone when gMethod == 'failed' then call DoFailed otherwise call Die 'method' gMethod 'unexpected' end exit RESULT /* end main */ /*=== DoBegin() Handle begin method ===*/ DoBegin: procedure expose (Globals) if gType \== 'http-01' then call Die 'DoBegin: type' gType 'unsupported' call LogWriteVTSC 'Creating' gTokenFile call SysFileDelete gTokenFile call lineout gTokenFile, gAuth call lineout gTokenFile return 0 /* end DoBegin */ /*=== DoDone() Handle done method ===*/ DoDone: procedure expose (Globals) call LogWriteVTSC 'DoDone deleting' gTokenFile call SysFileDelete gTokenFile return 0 /* end DoDone */ /*=== DoFailed() Handle failed method ===*/ DoFailed: procedure expose (Globals) call LogWriteVTSC 'DoFailed deleting' gTokenFile call SysFileDelete gTokenFile return 0 /* end DoFailed */ /*=== Initialize() Initialize globals ===*/ Initialize: procedure expose (Globals) call SetCmdName call LoadRexxUtil gEnv = 'OS2ENVIRONMENT' return /* end Initialize */ /*==============================================================================*/ /*=== SkelRexxFunc standards - Delete unused - Move modified above this mark ===*/ /*==============================================================================*/ /*=== DieVTS(message) Write message to STDOUT and timestampped message to log file and die ===*/ Die: /* Requires LogWriteVTS and dependents */ /* Requires LogWriteVTSC and dependents */ parse arg msg callerSIGL = SIGL /* Use say to avoid NotReady in case running detached - FIXME to be sure not ok to write to STDERR */ say call LogWriteVTS msg msg = gCmdName 'aborting at line' callerSIGL || '.' call LogWriteVTSC msg call beep 200, 300 exit 254 /* end DieVTS */ /*=== GetEnv(var) Return value for environment variable or empty string ===*/ GetEnv: procedure expose (Globals) parse arg var if var = '' then call Die 'GetEnv requires an argument' return value(var,, gEnv) /* end GetEnv */ /*=== IsDir(dirName[, full]) return true if directory is valid, accessible directory ===*/ IsDir: procedure /* If arg(2) not omitted, return full directory name or empty string */ parse arg dir, full newdir = '' do 1 if dir == '' then do cwd = '' /* No restore needed */ leave end dir = translate(dir, '\', '/') /* Convert to OS/2 slashes */ s = strip(dir, 'T', '\') /* Chop trailing slashes unless root */ if s \== '' & right(s, 1) \== ":" then dir = s /* Chop */ drv = filespec('D', dir) cwd = directory() /* Remember */ /* If have drive letter and requested directory on some other drive */ if drv \== '' & translate(drv) \== translate(left(cwd, 2)) then do /* Avoid slow failures and unwanted directory changes */ drvs = SysDriveMap('A:') if pos(translate(drv), drvs) = 0 then leave /* Unknown drive */ if SysDriveInfo(drv) == '' then leave /* Drive not ready */ cwd2 = directory(drv) /* Remember current directory on other drive */ newdir = directory(dir) /* Try to change and get full path name */ call directory cwd2 /* Restore current directory on other drive */ leave end /* If no drive letter or same drive and not UNC name */ if left(dir, 2) \== '\\' then do newdir = directory(dir) /* Try to change and get full path name */ leave end /* UNC name - hopefully server is accessible or this will be slow Accept \\server \\server\ \\server\dir\ \\server\dir */ cwd = '' /* No restore needed */ wc = dir if right(wc, 1) \== '\' then wc = wc || '\' i = lastpos('\', wc) if substr(wc, 3, 1) == '\' then leave /* Malformed UNC - no server name */ if pos('*', wc) > 0 | pos('?', wc) > 0 then leave /* No wildcards allowed */ call SysFileTree wc, 'files', 'O' if files.0 > 0 then do s = files.1 /* Exists and is not empty */ i = lastpos('\', s) newdir = left(s, i - 1) /* Extract directory name from full path name */ leave end /* Try wildcarded directory name */ wc = strip(wc, 'T', '\') i = lastpos('\', wc) base = substr(wc, i + 1) if base == '' then leave /* Should have matched above */ wc = substr(wc, 1, i) || '*' || base || '*' call SysFileTree wc, 'files', 'DO' do fileNum = 1 to files.0 /* Find directory name is list */ s = files.fileNum i = lastpos('\', s) s2 = substr(s, i + 1) if translate(base) == translate(s2) then do newdir = left(s, i - 1) leave end end /* i */ end /* 1 */ if cwd \== '' then call directory cwd /* Restore original directory and drive */ if full \== '' then ret = newdir /* Return full directory name or empty string */ else ret = newdir \== '' /* Return true if valid and accessible */ return ret /* end IsDir */ /*=== LogOpen() Open log file for append ===*/ LogOpen: procedure expose (Globals) /* Requires LogSetName unless gLogFile defined */ /* Sets gLogFile if not defined */ /* Overrides gLogFile if open fails */ if symbol('gLogFile') \== 'VAR' then call LogSetName /* Assume closed */ call stream gLogFile, 'C', 'OPEN WRITE' if stream(gLogFile) \== 'READY' then do gLogFile = '\' || gCmdName || '.log' /* Try root */ call stream gLogFile, 'C', 'OPEN WRITE' end return /* end LogOpen */ /*=== LogSetName() Set log file name ===*/ /** * Sets gLogFile if not defined * Sets gLogDir if not defined */ LogSetName: procedure expose (Globals) /* Requires LogSetDir unless gLogDir defined */ /* Requires gCmdName */ if symbol('gLogFile') \== 'VAR' then do if symbol('gLogDir') \== 'VAR' then call LogSetDir /* Ensure trailing backslash unless using current directory */ dir = gLogDir if dir \== '' & right(dir, 1) \== ':' & right(dir, 1) \== '\' then dir = dir || '\' /* Ensure trailing backslash */ gLogFile = dir || gCmdName'.log' end return /* end LogSetName */ /*=== LogSetDir() Set gLogDir and provide trailing backslash if needed ===*/ /** * Set gLogDir if gLogDir not defined * Tries %LOGFILES gTmpDir %TMP * Falls back to current directory and returns null string */ LogSetDir: procedure expose (Globals) if symbol('gLogDir') \== 'VAR' then do /* Try gLogDir %LOGFILES gTmpDir %TMP */ do 1 /* Try %LOGFILES */ gLogDir = value('LOGFILES',, gEnv) if gLogDir \== '' then leave /* Try gTmpDir */ if symbol('gTmpDir') == 'VAR' then do gLogDir = gTmpDir leave end /* Try %TMP - return empty string if TMP not defined */ gLogDir = value('TMP',, gEnv) end end return /* end LogSetDir */ /*=== LogWriteVTS(message,...) Write multi-line message to STDOUT and timestamped message to log file ===*/ LogWriteVTS: procedure expose (Globals) /* Requires LogOpen */ /* Requires MakeTimestamp */ if symbol('gLogFile') \== 'VAR' then call LogOpen do i = 1 to arg() say arg(i) call lineout gLogFile, MakeTimestamp() arg(i) if symbol('gLogWrites') == 'VAR' then gLogWrites = gLogWrites + 1 end return /* end LogWriteVTS */ /*=== LogWriteVTSC(message,...) Write multi-line message to STDOUT and timestamped message to log file and close log ===*/ LogWriteVTSC: procedure expose (Globals) /* Requires LogOpen */ /* Requires MakeTimestamp */ if symbol('gLogFile') \== 'VAR' then call LogOpen ts = MakeTimestamp() do i = 1 to arg() say ts arg(i) call lineout gLogFile, ts arg(i) if symbol('gLogWrites') == 'VAR' then gLogWrites = gLogWrites + 1 end call stream gLogFile, 'C', 'CLOSE' return /* end LogWriteVTSC */ /*=== MakePath(pathparts,...) Make path name from parts ===*/ MakePath: procedure /* All parts optional - code guesses what caller means. If last arg begins with a dot and is not .. and does not contain a slash, it is assumed to be a file extension. To avoid this behavior, pass empty arg as last arg. Empty args are ignored. Automatically converts unix slashes to dos slashes. If 1st arg is drive letter, it must have trailing colon. */ argCnt = arg() path = '' do argNum = 1 to argCnt s = arg(argNum) s = translate(s, '\', '/') /* Ensure DOS */ if s == '' & argNum = argCnt then iterate /* Ignore nul last arg */ if argNum = 1 then path = s else do lead = left(s, 1) tail = right(path, 1) if tail == ':' & argNum = 2 then path = path || s /* Append path part to drive spec */ else if lead == '.' & argNum = argCnt & s \== '..' & pos('\', s) = 0 then path = path || s /* Assume extension unless .. or contains \ */ else if tail == '\' & lead == '\' then path = path || substr(s, 2) /* Drop extra backslash */ else if path \== '' & tail \== '\' & lead \== '\' then path = path || '\' || s /* Ensure have backslash */ else path = path || s end end /* for */ return path /* end MakePath */ /*=== MakeTimestamp() Convert current date/time to sorted, delimited timestamp - yyyy/mm/dd-hh:mm:ss ===*/ MakeTimestamp: procedure /* Return yyyy/mm/dd-hh:mm:ss */ return translate('ABCD/EF/GH',date('S'),'ABCDEFGH')'-'time() /* end MakeTimestamp */ /*==========================================================================*/ /*=== SkelRexx standards - Delete unused - Move modified above this mark ===*/ /*==========================================================================*/ /*=== Error() Set gErrCondition; report to STDOUT; trace and exit or return if called ===*/ Error: say parse source . . cmd gErrCondition = condition('C') say gErrCondition 'signaled at line' SIGL 'of' cmd || '.' if condition('D') \== '' then say 'REXX reason =' condition('D') || '.' if gErrCondition == 'SYNTAX' & symbol('RC') == 'VAR' then say 'REXX error =' RC '-' errortext(RC) || '.' else if symbol('RC') == 'VAR' then say 'RC =' RC || '.' say 'Source =' sourceline(SIGL) if condition('I') \== 'CALL' | gErrCondition == 'NOVALUE' | gErrCondition == 'SYNTAX' then do trace '?A' say 'Enter REXX commands to debug failure. Press enter to exit script.' nop if symbol('RC') \== 'VAR' then RC = 255 exit RC end return /* end Error */ /*=== Halt() Report HALT condition to STDOUT and exit ===*/ Halt: say parse source . . cmd say condition('C') 'signaled at' cmd 'line' SIGL || '.' say 'Source =' sourceline(SIGL) say 'Sleeping for 2 seconds...' call SysSleep 2 exit 253 /* end Halt */ /*=== LoadRexxUtil() Load RexxUtil functions ===*/ LoadRexxUtil: if RxFuncQuery('SysLoadFuncs') then do call RxFuncAdd 'SysLoadFuncs', 'REXXUTIL', 'SysLoadFuncs' if RESULT then call Die 'Cannot load SysLoadFuncs.' call SysLoadFuncs end return /* end LoadRexxUtil */ /*=== SetCmdName() Set gCmdName to short script name ===*/ SetCmdName: procedure expose (Globals) parse source . . cmd cmd = filespec('N', cmd) /* Chop path */ c = lastpos('.', cmd) if c > 1 then cmd = left(cmd, c - 1) /* Chop extension */ gCmdName = translate(cmd, xrange('a', 'z'), xrange('A', 'Z')) /* Lowercase */ return /* end SetCmdName */ /* eof */