/*****************************************************************************/ /* VMS.c Eclectic collection of stubs, glue and miscellaneous functionality, primarily supporting acme-client-portable but also LibTLS and WCME in general. VERSION HISTORY --------------- 15-OCT-2017 MGD pairofsockets() for building on VMS < V8.2 23-APR-2017 MGD initial */ /*****************************************************************************/ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char *AcmePortDomainPtr = NULL, *AcmePortEsPtr = NULL; static int pipefds [10][2]; static char* subps[10] = { "netproc", /* COMP_NET */ "keyproc", /* COMP_KEY */ "certproc", /* COMP_CERT */ "acctproc", /* COMP_ACCOUNT */ "chngproc", /* COMP_CHALLENGE */ "fileproc", /* COMP_FILE */ "dnsproc", /* COMP_DNS */ "revokeproc" /* COMP_REVOKE */ }; /*****************************************************************************/ /* Stubs. */ int chroot (const char *path) { printf("chroot() |%s|\n",path);return 0; } char* getprogname() { return "WCME"; } int setgroups (int size, void *gid_t) { return 0; } int setegid (unsigned int gid_t) { return 0; } char *strsignal (int signal) { return "no-signal"; } /*****************************************************************************/ /* After zeroing free sensitive memory. */ void freezero (void* buf, int size) { memset (buf, 0, size); free (buf); } /*****************************************************************************/ /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4)) void * reallocarray (void *optr, int nmemb, int size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } /* Linux-isms */ /*****************************************************************************/ /* Error message. */ void verr (const char *format, va_list ap) { char fbuf [256]; if (AcmePortEsPtr) sprintf (fbuf, "%s [%s] %s\n", tstamp(NULL), AcmePortEsPtr, format); else sprintf (fbuf, "%s\n", format); vfprintf (stderr, fbuf, ap); } /*****************************************************************************/ /* Error message. */ void verrx (const char *format, va_list ap) { char fbuf [256]; if (AcmePortEsPtr) sprintf (fbuf, "%s [%s] %s\n", tstamp(NULL), AcmePortEsPtr, format); else sprintf (fbuf, "%s\n", format); vfprintf (stderr, fbuf, ap); } /*****************************************************************************/ /* Error message. */ void err (int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verr(fmt, ap); va_end(ap); } /*****************************************************************************/ /* Error message. */ void errx (int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrx(fmt, ap); va_end(ap); } /*****************************************************************************/ /* Warning message. */ void vwarn (const char *format, va_list ap) { char fbuf [256]; if (AcmePortEsPtr) sprintf (fbuf, "%s [%s] %s\n", tstamp(NULL), AcmePortEsPtr, format); else sprintf (fbuf, "%s\n", format); vfprintf (stderr, fbuf, ap); } /*****************************************************************************/ /* Warning message. */ void vwarnx (const char *format, va_list ap) { char fbuf [256]; if (AcmePortEsPtr) sprintf (fbuf, "%s [%s] %s\n", tstamp(NULL), AcmePortEsPtr, format); else sprintf (fbuf, "%s\n", format); vfprintf (stderr, fbuf, ap); } /*****************************************************************************/ /* Warning message. */ void warn (const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } /*****************************************************************************/ /* Warning message. */ void warnx (const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } /*****************************************************************************/ /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy (char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /*********/ /* begin */ /*********/ /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } /*****************************************************************************/ /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat (char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /*********/ /* begin */ /*********/ /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) #ifndef __VMS return(dlen + strlen(s)); #else { const char *sptr = s; while (*sptr) sptr++; return(dlen + (sptr - s)); } #endif while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } /*****************************************************************************/ /* Duplicate the string up to |n| characters. */ char* strndup(const char *s, size_t n) { char* new = malloc(n+1); if (new) { strncpy(new, s, n); new[n] = '\0'; } return new; } /*****************************************************************************/ /* The timingsafe_bcmp() and timingsafe_memcmp() functions lexicographically compare the first len bytes (each interpreted as an unsigned char) pointed to by b1 and b2. Additionally, their running times are independent of the byte sequences compared, making them safe to use for comparing secret values such as cryptographic MACs. In contrast, bcmp(3) and memcmp(3) may short-circuit after finding the first differing byte. */ int timingsafe_memcmp (const void *b1, const void *b2, size_t len) { size_t i; int res = 0, done = 0; const unsigned char *p1 = b1, *p2 = b2; /*********/ /* begin */ /*********/ for (i = 0; i < len; i++) { /* lt is -1 if p1[i] < p2[i]; else 0. */ int lt = (p1[i] - p2[i]) >> CHAR_BIT; /* gt is -1 if p1[i] > p2[i]; else 0. */ int gt = (p2[i] - p1[i]) >> CHAR_BIT; /* cmp is 1 if p1[i] > p2[i]; -1 if p1[i] < p2[i]; else 0. */ int cmp = lt - gt; /* set res = cmp if !done. */ res |= cmp & ~done; /* set done if p1[i] != p2[i]. */ done |= lt | gt; } return (res); } /*****************************************************************************/ /* Find the first occurrence of the byte string s in byte string l. */ void* memmem (const void *l, size_t l_len, const void *s, size_t s_len) { const char *cur, *last; const char *cl = l; const char *cs = s; /* a zero length needle should just return the haystack */ if (s_len == 0) return (void *)cl; /* "s" must be smaller or equal to "l" */ if (l_len < s_len) return NULL; /* special case where s_len == 1 */ if (s_len == 1) return memchr (l, *cs, l_len); /* the last position where its possible to find "s" in "l" */ last = cl + l_len - s_len; for (cur = cl; cur <= last; cur++) if (cur[0] == cs[0] && memcmp (cur, cs, s_len) == 0) return (void *)cur; return NULL; } /*****************************************************************************/ /* Takes a broken-down time and convert it to calendar time (seconds since the Epoch, 1970-01-01 00:00:00 +0000, UTC). */ time_t timegm(struct tm * a_tm) { time_t ltime = mktime(a_tm); struct tm tm_val; gmtime_s(&tm_val, <ime); int offset = (tm_val.tm_hour - a_tm->tm_hour); if (offset > 12) offset = 24 - offset; time_t utc = mktime(a_tm) - offset * 3600; return utc; } /*****************************************************************************/ /* As above. */ int gmtime_s (const time_t* timer, struct tm* result) { gmtime_r (timer, result); return (errno); } /*****************************************************************************/ /* Implementation of fork()/exec..() for this application. SYSPRV is enabled so the subprocess inherits as an authorised privilege. The scripting process account (unprivileged) requires this. */ int furk (const char* what, const char* newargs[]) { static int net_fds [2]; int pid, retval; /*********/ /* begin */ /*********/ UtilSysPrv(); pid = vfork (); if (pid == 0) { newargs[2] = what; retval = execvp (newargs[0], (char**)newargs); if (retval == -1) { UtilMereMortal(); warn("%s %s", what, newargs[0]); return (-1); } } UtilMereMortal(); #ifdef WCMEDBGVMS doddbg("furk() %s %08.08X", what, pid); #endif return (pid); } /*****************************************************************************/ /* Roll our own pair-of-sockets (socketpair()) function. Intended to work-around C-RTLs less than VMS V8.2. */ /** #include #include #include **/ #include #include int pairofsockets (int sd[2]) { int sock0, sock1, sock2; unsigned int len; struct sockaddr_in sock_in, sock_out; sock0 = socket (AF_INET, SOCK_STREAM, 0); if (sock0 <= 0) return (-1); /* localhost dynamic port */ sock_in.sin_family = AF_INET; sock_in.sin_port = 0; sock_in.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind (sock0, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0) { close (sock0); return (-1); } /* get that dynamic port */ len = sizeof(sock_in); if (getsockname (sock0, (struct sockaddr*)&sock_in, &len) < 0) { close (sock0); return (-1); } /* make this socket a listener */ listen (sock0, 2); sock2 = socket (AF_INET, SOCK_STREAM, 0); if (sock2 <= 0) { close (sock0); return (-1); } /* connect to the listener */ if (connect (sock2, (struct sockaddr*)&sock_in, sizeof(sock_in)) < 0) { close (sock0); return (-1); } /* accept the connection and close the listener */ len = sizeof (sock_in); if ((sock1 = accept (sock0, (struct sockaddr*) &sock_in, &len)) <= 0) { close (sock0); close (sock2); return (-1); } close (sock0); /* return the socket pair */ sd[0] = sock1; sd[1] = sock2; return (0); } /*****************************************************************************/ /* Don't throw the terminal for a six. Excise non-printables a la buf_dump(). */ char* exctrl (char* sptr, int slen) { static char *bptr; char *zptr; /*********/ /* begin */ /*********/ if (bptr) free (bptr); if (slen <= 0) { for (bptr = sptr; *bptr; bptr++); slen = bptr - sptr; } if (!slen) return (""); bptr = calloc(1, slen); memcpy (bptr, sptr, slen); for (zptr = (sptr = bptr) + slen; sptr < zptr; sptr++) if (!isprint(*sptr) && *sptr != '\r' && *sptr != '\n') *sptr = '?'; return (bptr); } /*****************************************************************************/ /* Return a pointer to string time-stamp "yyyy-mm-dd-hh:mm:ss.hh". Not reentrant. */ char* tstamp (ulong *binptr) { static char stamp [32]; ulong BinTime [2]; ushort NumTime [7]; /*********/ /* begin */ /*********/ if (!binptr) sys$gettim (binptr = (ulong*)&BinTime); sys$numtim (&NumTime, binptr); sprintf (stamp, "%04.04d-%02.02d-%02.02d %02.02d:%02.02d:%02.02d.%02.02d", NumTime[0], NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6]); return (stamp); } /*****************************************************************************/ /* Same as doddbg() but without the -v[v] test. */ void vmsdbg (char *fmt, ...) { va_list ap; va_start (ap, fmt); vwarnx (fmt, ap); va_end (ap); } /*****************************************************************************/ /* Debug tool to list the allocated file descriptors. */ void fdlist (int which, const char* what) { int ret; /*********/ /* begin */ /*********/ fprintf (stderr, "fdlist() %d %s\n", which, (char*)what); for (int cnt = 0; cnt <= 20; cnt++) if (-1 != (ret = fcntl(cnt, F_GETFL))) fprintf (stderr, "%d %08.08x %d\n", cnt, ret, decc$get_sdc(cnt)); for (int cnt = 50; cnt <= 60; cnt++) if (-1 != (ret = fcntl(cnt, F_GETFL))) fprintf (stderr, "%d %s %08.08x %d\n", cnt, subps[cnt-50], ret, decc$get_sdc(cnt)); } /*****************************************************************************/ /* Debug tool providing which process the file descriptor belongs to. */ char* fdid (int fd) { static char buf [256]; sprintf (buf, "%d %s", fd, subps[fd-50]); return buf; } /*****************************************************************************/ /* Debug tool to list the arguments. */ void arglist (char* where, char *argv[]) { fprintf (stdout, "%s:\n", where); for (int cnt = 0; argv[cnt]; cnt++) fprintf (stdout, "argv[%d] |%s|\n", cnt, argv[cnt]); } /*****************************************************************************/