/*****************************************************************************/ /* authagent_LDAP_2.c *** THIS VERSION USES IT'S OWN CODE TO DO THE AUTHENTICATION *** LDAP authentication agent CGIplus script, using the callout mechanism. Code adapted from the OpenVMS Utility Routines Manual, Sample LDAP API Code. Uses the integrated LDAP run-time support available with VMS V7.3 and later. Requires the HP SSL Package to be installed and started. Attempts to improve performance by remaining connected to the last accessed LDAP server. If the server host or port changes between authentication requests (which is generally unlikely) the agent unbinds from the currently connected LDAP server before connecting to the new. If there are any 'hard' errors while processing (e.g. broken connection) the agent unbinds and retries. Parameters can be passed in the command-line or the HTTPD$AUTH param= directive, or a combination of both. Agent persistence means parameters set remain set until use of the same parameter (qualifier) again overwriting the previous value, or until the /RESET qualifier is encountered. This resets the values of *all* parameters to the default values. Hence, basic parameters such as host and port, and perhaps simple binding parameters, may be established at the command-line (perhaps using a wrapper DCL procedure) and then each authorization request could vary that as necessary using HTTPD$AUTH param= directives. Care must be exercised that one does not interfere with another. In general though most LDAP agent parameters can just be passed through from the authorization rule with each request (see example below). The filter implements the '%u' and '%d' conversion characters to allow complex expressions to be built using the userid and realm from the user credentials. Will verify the 'password' attribute returned either: 1) against the SYSUAF using $GETUAI and $HASH_PASSWORD to hash the password supplied with the request and compare it to the SUSUAF entry. 2) using the 'userPassword' attribute returned to compute a local digest and compare that to the digest in the attribute. Current digests supported: a) {SHA} SHA1 (base-64 encoded) b) {SSHA} salted SHA1 (base-64 encoded) c) {MD5} MD5 (base-64 encoded) d) {SMD5} salted MD5 (base-64 encoded) e) plain-text If the password verifies the 'uid' is used as the VMS username. If the entry did not contain a 'uid' and a default username has been configured (using the /UDEF="" parameter) this is substituted as the authenticated VMS username. Note that (for the obvious Catch-22) the password cannot be one validated using the /SYSUAF facility. RUN-TIME PARAMETERS ------------------- These parameters are used to pass run-time information to the authentication agent from the authorization configuration (see Authentication Configuration below). /BASE= ldap_search_s() 'base' string /CERT= client certificate and private key (if files are protected may need to be INSTALLed with SYSPRV) /DATT= LDAP attribute containing the user detail for the AUTH_USER variable (defaults to 'displayName') /DIGEST verify the returned username and supplied password by matching the digested password locally /FILTER= ldap_search_s() 'filter' string (implements the '%u' and '%d' conversions) /LOCAL verify username through locally supplied function /PATT= LDAP attribute name containing (encoded) password (defaults to 'userPassword' attribute) /PORT= optional method for specifying LDAP server port /RESET reset the parameters (in persistent environments parameters can remain across uses) /SERVER= host name and optional port number for LDAP server /SIMPLE= account and ':'-separated password for simple bind /SSL[=] use LDAPS (deprecated SSL) and optionally specify the SSL protocol (default 1; 20, 23, 30, 31, etc) /SYSUAF verify the returned username and supplied password using $GETUAI and $HASH_PASSWORD /TLS[=] use LDAP TLS (current SSL) and optionally specify the SSL protocol (default 1; 20, 23, 30, 31, etc.) /UDEF= default VMS username when entry found and password validated but no uid available /UATT= LDAP attribute name containing VMS username (defaults to 'uid' attribute) /VERSION= LDAP protocol version (defaults to 3) COMMAND-LINE PARAMETERS ----------------------- In addition to the run-time parameters these are available at the command-line. /AUTH_PASSWORD= emulates CGI environment AUTH_PASSWORD variable /DBUG low level debug statements /DUMP output the detail of matching entries /REMOTE_USER= emulates CGI environment REMOTE_USER variable /WATCH output WASD server WATCH-able statements AUTHENTICATION CONFIGURATION ---------------------------- The run-time parameters CAN be passed via a 'param=' argument to the HTTPD$AUTH configuration for the realm. ["Just an LDAP Example"=AUTHAGENT_LDAP=agent] /an/example/path/* r+w,param='\ /HOST="ldap.host.domain"/BASE="dc=domain,DC=au"\ /FILTER="uid=%u"/SIMPLE="account:password"' COMMAND-LINE CHECKING --------------------- Most functionality may be checked from the command-line using (almost) exactly the same syntax and parameters as used in the authentication (CGIplus) mode. This allows the format and syntax of the authentication configuration to be developed and tested interactively. In addition, and for general information, queries not possible (e.g. using wildcards) in the authentication mode may be used in the command-line mode. $ LDAPAGE == "$WASD_EXE:AUTHAGENT_LDAP /WATCH" $ LDAPAGE /DUMP /HOST="ldap.fnal.gov" /BASE="o=fnal" /FILTER="cn=*smith*" $ LDAPAGE /HOST="ldap.host.domain" /BASE="dc=domain,DC=au" - /FILTER="uid=%u" /SIMPLE="account:password" - /REMOTE_USER="whatever" PRIVILEGED IMAGE ---------------- To use the /SYSUAF qualifier (and $GETUAI) for verifying the supplied password the image must be installed with SYSPRV. $ INSTALL REPLACE :AUTHAGENT_LDAP.EXE /AUTHPRIV=SYSPRV LOGICAL NAMES ------------- AUTHAGENT_LDAP$DBUG same as /DBUG AUTHAGENT_LDAP$WATCH same as /WATCH BUILD DETAILS ------------- Compile then link: $ @BUILD_AUTHAGENT_LDAP To just link: $ @BUILD_AUTHAGENT_LDAP LINK SPONSOR ------- This software has been developed under the sponsorship of the University of Malaga and generously made available to the wider WASD community. Many thanks. COPYRIGHT --------- Copyright (C) 2006-2021 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY (update SOFTWAREVN as well) --------------- 10-JUN-2021 MGD v2.0.0, minimal changes for WASD v12... agent requirements 04-MAY-2017 MGD v1.0.3, upper-case kludge to work around SSL1 link issues (needs a lot more effort to make work with OSSL$) 11-MAY-2007 MGD v1.0.2, belt-and-braces 16-JUL-2006 MGD v1.0.1, add /DATT and user details callout 08-JUL-2006 MGD v1.0.0, initial development */ /*****************************************************************************/ #ifndef USE_OPENSSL #define USE_OPENSSL 1 #endif #ifdef USE_OPENSSL #define USING_OPENSSL " (SSL)" #else #define USING_OPENSSL " (sans SSL)" #endif #define SOFTWAREVN "2.0.0" #define SOFTWARENM "AUTHAGENT_LDAP" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN USING_OPENSSL #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN USING_OPENSSL #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* standard C header files */ #include #include #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include #include #include /* application header files */ #include #include #if USE_OPENSSL /* upper-case kludge to work around SSL1 link issues */ #define BIO_ctrl BIO_CTRL #define BIO_f_base64 BIO_F_BASE64 #define BIO_new BIO_NEW #define BIO_push BIO_PUSH #define BIO_read BIO_READ #define BIO_s_mem BIO_S_MEM #define BIO_set_flags BIO_SET_FLAGS #define BIO_write BIO_WRITE #define MD5_Final MD5_FINAL #define MD5_Init MD5_INIT #define MD5_Update MD5_UPDATE #define SHA1_Final SHA1_FINAL #define SHA1_Init SHA1_INIT #define SHA1_Update SHA1_UPDATE /* OpenSSL header files (0.9.3ff) */ #include "openssl/err.h" #include "openssl/md5.h" #include "openssl/X509.h" #include "openssl/ssl.h" #include "openssl/bio.h" #include "openssl/buffer.h" #include "openssl/crypto.h" #endif /* USE_OPENSSL */ #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define BOOL int #define TRUE 1 #define FALSE 0 #define DEFAULT_ATTRIBUTE_USERNAME "uid" #define DEFAULT_ATTRIBUTE_PASSWORD "userPassword" #define DEFAULT_ATTRIBUTE_DISPLAY "displayName" #define DEFAULT_SSL_PORT 636 /******************/ /* global storage */ /******************/ char Utility [] = "AUTHAGENT_LDAP"; BOOL Debug, DebugWatch, DoDump, LdapSimpleBind, VerifyDigest, VerifyLocal, VerifySYSUAF; int CgiPlusUsageCount, LdapProtocolVersion = LDAP_VERSION3, LdapSSL, StartTLS; unsigned short LdapPort; char *AuthAgentPtr, *RemoteUserPtr; char CertKeyFileName [128], CliRemoteUser [48], CliAuthPassword [48], LdapBase [128], LdapFilter [128], LdapHost [128], LdapSimpleBindDn [128], LdapSimpleBindPasswd [64], PasswordAttribute [64] = DEFAULT_ATTRIBUTE_PASSWORD, PasswordValue [96], PasswordHash [128], SoftwareId [96], UserDisplayAttribute [64] = DEFAULT_ATTRIBUTE_DISPLAY, UserDisplayValue [64], UserNameAttribute [64] = DEFAULT_ATTRIBUTE_USERNAME, UserNameDefault [64], UserNameValue [64], VmsUserName [48]; unsigned long SysPrvMask [2] = { PRV$M_SYSPRV, 0 }; /***********************/ /* function prototypes */ /***********************/ int DecodeBase64 (char*, int, char*, int); void GetParameters (); void GetRunTimeParameters (char*, BOOL); void HashMD5 (char*, int, char*, int, char*); void HashSHA1 (char*, int, char*, int, char*); void NeedsPrivilegedAccount (); BOOL ProcessRequest (); void RemoveSlash (char*); char* SysTrnLnm (char*, char*); BOOL VerifyAgainstSYSUAF (char*, char*); BOOL VerifyLocalFunction (char*, char*); BOOL VerifyMD5 (char*, char*); BOOL VerifySMD5 (char*, char*); BOOL VerifySHA (char*, char*); BOOL VerifySSHA (char*, char*); char* WatchElapsed (); void WatchThis (int, char*, ...); int strzcpy (char*, char*, int); BOOL strsame (char*, char*, int); /*****************************************************************************/ /* */ main () { BOOL ok; int status; char *cptr; /*********/ /* begin */ /*********/ sprintf (SoftwareId, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion()); Debug = (SysTrnLnm ("AUTHAGENT_LDAP$DBUG", NULL) != NULL); CgiLibEnvironmentSetDebug (Debug); GetParameters (); CgiLibEnvironmentInit (0, NULL, FALSE); if (!SysTrnLnm ("HTTP$INPUT", NULL)) { /* not in a server context (interactive, command-line testing) */ NeedsPrivilegedAccount (); if (DebugWatch) WatchElapsed (); ProcessRequest (); if (DebugWatch) WatchThis (__LINE__, "ELAPSED %s", WatchElapsed()); exit (SS$_NORMAL); } /* MUST only be executed in a CGIplus environment! */ if (!CgiLibEnvironmentIsCgiPlus ()) { CgiLibResponseHeader (502, "text/plain"); fputs ("CGIplus!\n", stdout); exit (SS$_NORMAL); } for (;;) { /* block waiting for the next request */ CgiLibVar (""); CgiPlusUsageCount++; if (cptr = CgiLibVarNull("REQUEST_METHOD")) if (!*cptr) { /* proctored into existance */ CgiLibResponseHeader (204, "application/proctor"); CgiLibCgiPlusEOF (); continue; } CgiLibCgiPlusCallout ("!AGENT-BEGIN: %s (%s) usage:%d", SoftwareId, CgiLibEnvironmentVersion(), CgiPlusUsageCount); /* provide the server attention "escape" sequence record */ if (!Debug) CgiLibCgiPlusESC (); DebugWatch = (SysTrnLnm ("AUTHAGENT_LDAP$WATCH", NULL) != NULL); if (DebugWatch) WatchElapsed (); /* ensure this is being invoked by the server */ if (!(AuthAgentPtr = CgiLibVarNull ("AUTH_AGENT"))) exit (SS$_ABORT); if (DebugWatch) WatchThis (__LINE__, "AUTH_AGENT %s", AuthAgentPtr); /* belt and braces */ fprintf (stdout, "100 AUTHAGENT-CALLOUT\n"); fflush (stdout); /* have at least two goes at authenticating the user */ if (!(ok = ProcessRequest ())) ok = ProcessRequest (); if (!ok) { fprintf (stdout, "500 LDAP authenticator.\n"); fflush (stdout); } if (DebugWatch) WatchThis (__LINE__, "ELAPSED %s", WatchElapsed()); /* provide the "escape" end-of-text sequence record */ if (!Debug) CgiLibCgiPlusEOT (); CgiLibCgiPlusCallout ("!AGENT-END:"); CgiLibCgiPlusEOF (); if (!ok) exit (SS$_NORMAL); } } /*****************************************************************************/ /* Main authentication request processing function. */ BOOL ProcessRequest () { static BOOL ServerUnbind; static char ConnectedLdapHost [128]; static short ConnectedLdapPort; static LDAP *ld; BOOL PasswordOK; int idx, rc, status, EntryCount; char *cptr, *sptr, *tptr, *zptr, *PasswordPtr; char UserFilter [256], UserId [64], UserRealm [128]; LDAPMessage *resptr, *entptr; char *attptr, *dnptr; BerElement *berptr; char **AttValues; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequest()\n"); if (DebugWatch) WatchThis (__LINE__, "%s", SoftwareId); if (AuthAgentPtr) GetRunTimeParameters (AuthAgentPtr, FALSE); if (CgiLibEnvironmentIsCgiPlus()) { RemoteUserPtr = CgiLibVarNull ("REMOTE_USER"); if (!RemoteUserPtr) { if (DebugWatch) WatchThis (__LINE__, "REMOTE_USER?"); return (FALSE); } } else RemoteUserPtr = CliRemoteUser; /* ensure no covert expressions sneak in under the radar */ for (cptr = RemoteUserPtr; *cptr; cptr++) if (*cptr == '*' || *cptr == '&' || *cptr == '|' || *cptr == '!' || *cptr == '(' || *cptr == ')' || *cptr == '\\') break; if (*cptr) { if (DebugWatch) WatchThis (__LINE__, "REMOTE_USER wildcard/expression!"); fprintf (stdout, "401 ambiguous credentials.\n"); fflush (stdout); return (TRUE); } /********************/ /* build the filter */ /********************/ zptr = (sptr = UserId) + sizeof(UserId)-1; for (cptr = RemoteUserPtr; *cptr && *cptr != '@' && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (*cptr) cptr++; zptr = (sptr = UserRealm) + sizeof(UserRealm)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; /* implement '%u' for userid and '%d' for realm */ zptr = (sptr = UserFilter) + sizeof(UserFilter)-1; cptr = LdapFilter; while (*cptr && sptr < zptr) { while (*cptr && *cptr != '%' && sptr < zptr) *sptr++ = *cptr++; if (!*cptr) break; cptr++; if (!*cptr) break; if (*cptr == 'u') { cptr++; for (tptr = UserId; *tptr && sptr < zptr; *sptr++ = *tptr++); } else if (*cptr == 'd') { cptr++; for (tptr = UserRealm; *tptr && sptr < zptr; *sptr++ = *tptr++); } else /* unknown conversion character, just ignore it */ *sptr++ = *cptr++; } *sptr = '\0'; /* just override the above if from the command-line and no remote user */ if (!CgiLibEnvironmentIsCgiPlus() && !CliRemoteUser[0]) strzcpy (UserFilter, LdapFilter, sizeof(UserFilter)); if (DebugWatch) WatchThis (__LINE__, "FILTER |%s|", UserFilter); /******************************/ /* connect to the LDAP server */ /******************************/ /* check if we need to change any persistent connection */ if ((ConnectedLdapHost[0] && strcmp (LdapHost, ConnectedLdapHost)) || (ConnectedLdapPort && ConnectedLdapPort != LdapPort)) ServerUnbind = TRUE; if (ServerUnbind) { if (DebugWatch) WatchThis (__LINE__, "ldap_unbind() %s:%d", ConnectedLdapHost, ConnectedLdapPort); rc = ldap_unbind (ld); if (DebugWatch) if (rc != LDAP_SUCCESS) WatchThis (__LINE__, "ERROR %d %d %%X%08.08X %s", rc, errno, vaxc$errno, ldap_err2string(rc)); ServerUnbind = FALSE; ConnectedLdapHost[0] = '\0'; ConnectedLdapPort = 0; } if (ConnectedLdapHost[0]) { /* already connected to the required server */ if (DebugWatch) WatchThis (__LINE__, "persistent %s:%d", ConnectedLdapHost, ConnectedLdapPort); } else { strzcpy (ConnectedLdapHost, LdapHost, sizeof(ConnectedLdapHost)); ConnectedLdapPort = LdapPort; if (!ConnectedLdapPort) if (!LdapSSL) ConnectedLdapPort = LDAP_PORT; else ConnectedLdapPort = DEFAULT_SSL_PORT; if (DebugWatch) WatchThis (__LINE__, "ldap_init() %s:%d", ConnectedLdapHost, ConnectedLdapPort); ld = ldap_init (ConnectedLdapHost, ConnectedLdapPort); if (ld == NULL) { if (DebugWatch) WatchThis (__LINE__, "ERROR %d %%X%08.08X", errno, vaxc$errno); ServerUnbind = TRUE; return (FALSE); } ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &LdapProtocolVersion); if (StartTLS || LdapSSL) { /******************/ /* secure sockets */ /******************/ ldap_set_option (ld, LDAP_OPT_TLS_VERSION, StartTLS ? &StartTLS : &LdapSSL); if (CertKeyFileName[0]) { ldap_set_option (ld, LDAP_OPT_TLS_CERT_FILE, CertKeyFileName); ldap_set_option (ld, LDAP_OPT_TLS_PKEY_FILE, CertKeyFileName); /* turn on SYSPRV to allow access to possibly protected files */ status = sys$setprv (1, &SysPrvMask, 0, 0); if (DebugWatch) if (VMSnok(status) || status == SS$_NOTALLPRIV) WatchThis (__LINE__, "sys$setprv() %%X%08.08X\n", status); } if (DebugWatch) WatchThis (__LINE__, "ldap_tls_start() %s:%d", StartTLS ? "StartTLS" : "LDAPS", StartTLS ? StartTLS : LdapSSL); rc = ldap_tls_start (ld, StartTLS ? 1 : 0); /* turn off SYSPRV */ if (CertKeyFileName[0]) sys$setprv (0, &SysPrvMask, 0, 0); if (rc != LDAP_SUCCESS) { if (DebugWatch) WatchThis (__LINE__, "ERROR %d %d %%X%08.08X %s", rc, errno, vaxc$errno, ldap_err2string(rc)); ServerUnbind = TRUE; return (FALSE); } } if (LdapSimpleBind) { /* authenticate with simple username (well, dn=) and password */ if (DebugWatch) WatchThis (__LINE__, "ldap_simple_bind_s() |%s|%s|", LdapSimpleBindDn ? LdapSimpleBindDn : "(NULL)", LdapSimpleBindPasswd ? LdapSimpleBindPasswd : "(NULL)"); rc = ldap_simple_bind_s (ld, LdapSimpleBindDn, LdapSimpleBindPasswd); if (rc != LDAP_SUCCESS) { if (DebugWatch) WatchThis (__LINE__, "ERROR %d %d %%X%08.08X %s", rc, errno, vaxc$errno, ldap_err2string(rc)); ServerUnbind = TRUE; return (FALSE); } } } /******************/ /* make the query */ /******************/ if (DebugWatch) WatchThis (__LINE__, "ldap_search_s() |%s|%s|", LdapBase, UserFilter); rc = ldap_search_s (ld, LdapBase, LDAP_SCOPE_SUBTREE, UserFilter, NULL, 0, &resptr); if (rc != LDAP_SUCCESS) { if (DebugWatch) WatchThis (__LINE__, "ERROR %d %d %%X%08.08X %s", rc, errno, vaxc$errno, ldap_err2string(rc)); ServerUnbind = TRUE; return (FALSE); } /*******************/ /* process results */ /*******************/ PasswordValue[0] = UserDisplayValue[0] = UserNameValue[0] = '\0'; EntryCount = 0; for (entptr = ldap_first_entry (ld, resptr); entptr != NULL; entptr = ldap_next_entry (ld, entptr)) { EntryCount++; dnptr = ldap_get_dn (ld, entptr); if (DoDump) fprintf (stdout, "\n%03.03d |%s|\n", EntryCount, dnptr); ldap_memfree (dnptr); for (attptr = ldap_first_attribute (ld, entptr, &berptr); attptr != NULL; attptr = ldap_next_attribute (ld, entptr, berptr)) { if (DoDump) fprintf (stdout, " |->|%s|\n", attptr); AttValues = ldap_get_values (ld, entptr, attptr); for (idx = 0; AttValues[idx] != NULL; idx++) { if (!strcmp (PasswordAttribute, attptr)) { strzcpy (PasswordValue, AttValues[idx], sizeof(PasswordValue)); if (DebugWatch) WatchThis (__LINE__, "MATCH |%s|%s|", PasswordAttribute, PasswordValue); } else if (!strcmp (UserDisplayAttribute, attptr)) { strzcpy (UserDisplayValue, AttValues[idx], sizeof(UserDisplayValue)); if (DebugWatch) WatchThis (__LINE__, "MATCH |%s|%s|", UserDisplayAttribute, UserDisplayValue); } else if (!strcmp (UserNameAttribute, attptr)) { strzcpy (UserNameValue, AttValues[idx], sizeof(UserNameValue)); if (DebugWatch) WatchThis (__LINE__, "MATCH |%s|%s|", UserNameAttribute, UserNameValue); } if (DoDump) { if (!idx) fprintf (stdout, " | |->|%s|\n", AttValues[idx]); else fprintf (stdout, " | |%s|\n", AttValues[idx]); } } ldap_value_free (AttValues); ldap_memfree (attptr); } if (berptr != NULL) ber_free (berptr, 0); } if (DoDump) fprintf (stdout, "%s%d entries\n\n", EntryCount ? "\n" : "", EntryCount); if (DebugWatch) WatchThis (__LINE__, "ENTRIES %d", EntryCount); /* free the search results (let's hope it doesn't leak too much!) */ ldap_msgfree (resptr); if (EntryCount == 1) { /*******************/ /* found one entry */ /*******************/ if (!PasswordValue[0]) { /***********************/ /* password attribute? */ /***********************/ /* can't do much in the way of authentication without this! */ if (DebugWatch) WatchThis (__LINE__, "PASSWORD? (/PATT=)"); fprintf (stdout, "500 ambiguous LDAP result.\n"); } else if (!UserNameValue[0] && !UserNameDefault[0]) { /***********************/ /* username attribute? */ /***********************/ /* no username ('uid') and no default to fall back on */ if (DebugWatch) WatchThis (__LINE__, "USERNAME? (/UATT=)"); fprintf (stdout, "500 ambiguous LDAP result.\n"); } else { PasswordOK = FALSE; if (CliAuthPassword[0]) PasswordPtr = CliAuthPassword; else PasswordPtr = CgiLibVarNull ("AUTH_PASSWORD"); if (!PasswordPtr) { if (DebugWatch) WatchThis (__LINE__, "AUTH_PASSWORD?"); return (FALSE); } if (VerifyDigest) { /*****************************/ /* verify password by digest */ /*****************************/ #if USE_OPENSSL if (!memcmp (PasswordValue, "{SSHA}", 6)) { if (DebugWatch) WatchThis (__LINE__, "SSHA %s", PasswordValue+6); PasswordOK = VerifySSHA (PasswordValue, PasswordPtr); } else if (!memcmp (PasswordValue, "{SHA}", 5)) { if (DebugWatch) WatchThis (__LINE__, "SHA %s", PasswordValue+5); PasswordOK = VerifySHA (PasswordValue, PasswordPtr); } else if (!memcmp (PasswordValue, "{SMD5}", 6)) { if (DebugWatch) WatchThis (__LINE__, "SMD5 %s", PasswordValue+6); PasswordOK = VerifySMD5 (PasswordValue, PasswordPtr); } else if (!memcmp (PasswordValue, "{MD5}", 5)) { if (DebugWatch) WatchThis (__LINE__, "MD5 %s", PasswordValue+5); PasswordOK = VerifyMD5 (PasswordValue, PasswordPtr); } else #endif /* USE_OPENSSL */ /* dangling 'else' from the macro */ if (PasswordValue[0] != '{') { /* plain-text!! */ PasswordOK = (strcmp (PasswordValue, PasswordPtr) == 0); } else if (DebugWatch) WatchThis (__LINE__, "ENCODING unknown"); } else if (VerifySYSUAF) { /********************************/ /* verify password using SYSUAF */ /********************************/ PasswordOK = VerifyAgainstSYSUAF (UserNameValue, PasswordPtr); } else if (VerifyLocal) { /************************************/ /* verify using local functionality */ /************************************/ PasswordOK = VerifyLocalFunction (UserNameValue, PasswordPtr); } else { /*****************************/ /* must be verified somehow! */ /*****************************/ if (DebugWatch) WatchThis (__LINE__, "VERIFY?"); } /************/ /* verified */ /************/ if (PasswordOK) { if (!UserNameValue[0]) { if (DebugWatch) WatchThis (__LINE__, "DEFAULT %s", UserNameDefault); strzcpy (UserNameValue, UserNameDefault, sizeof(UserNameValue)); } fprintf (stdout, "100 VMS-USER %s\n", UserNameValue); if (UserDisplayValue[0]) { /* must be done following VMS-USER which also sets USER! */ fflush (stdout); fprintf (stdout, "100 USER %s\n", UserDisplayValue); } } else fprintf (stdout, "401 authentication failure.\n"); } } else if (EntryCount > 1) { /********************/ /* ambiguous result */ /********************/ if (DebugWatch) WatchThis (__LINE__, "AMBIGUOUS"); fprintf (stdout, "500 ambiguous LDAP result.\n"); } else { /*************/ /* not found */ /*************/ fprintf (stdout, "401 authentication failure.\n"); } /* flush the callout record */ fflush (stdout); return (TRUE); } /*****************************************************************************/ /* Verify the supplied username/password from the SYSUAF. Get the specified user's flags, authorized privileges, quadword password, hash salt and encryption algorithm from SYSUAF. If any of specified bits in the flags are set (e.g. "disusered") then fail the password authentication. Using the salt and encryption algorithm hash the supplied password and compare it to the UAF hashed password. Return TRUE if the password is validated and there are no account restrictions, FALSE otherwise. */ BOOL VerifyAgainstSYSUAF ( char *UserNamePtr, char *PasswordPtr ) { /* UAI flags that disallow SYSUAF authentication */ unsigned long DisallowVmsFlags = UAI$M_DISACNT | UAI$M_PWD_EXPIRED | UAI$M_PWD2_EXPIRED | UAI$M_CAPTIVE | UAI$M_RESTRICTED | 0; static unsigned long Context = -1; static unsigned long UaiFlags, UaiUic; static unsigned long UaiPriv [2], HashedPwd [2], UaiPwd [2]; static unsigned short UaiSalt; static unsigned char UaiEncrypt; static char UserName [15+1], Password [39+1]; static $DESCRIPTOR (UserNameDsc, UserName); static $DESCRIPTOR (PasswordDsc, Password); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } UaiItems [] = { { sizeof(UaiUic), UAI$_UIC, &UaiUic, 0 }, { sizeof(UaiFlags), UAI$_FLAGS, &UaiFlags, 0 }, { sizeof(UaiPriv), UAI$_PRIV, &UaiPriv, 0 }, { sizeof(UaiPwd), UAI$_PWD, &UaiPwd, 0 }, { sizeof(UaiEncrypt), UAI$_ENCRYPT, &UaiEncrypt, 0 }, { sizeof(UaiSalt), UAI$_SALT, &UaiSalt, 0 }, { 0, 0, 0, 0 } }; int status; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VerifyAgainstSYSUAF()\n"); /* to upper case! (just truncate if too long) */ zptr = (sptr = UserName) + sizeof(UserName)-1; for (cptr = UserNamePtr; *cptr && sptr < zptr; *sptr++ = toupper(*cptr++)); *sptr = '\0'; UserNameDsc.dsc$w_length = sptr - UserName; if (DebugWatch) WatchThis (__LINE__, "SYSUAF |%s|", UserName); /* turn on SYSPRV to allow access to SYSUAF records */ status = sys$setprv (1, &SysPrvMask, 0, 0); if (DebugWatch) if (VMSnok(status) || status == SS$_NOTALLPRIV) WatchThis (__LINE__, "sys$setprv() %%X%08.08X\n", status); status = sys$getuai (0, &Context, &UserNameDsc, &UaiItems, 0, 0, 0); if (Debug) fprintf (stdout, "sys$getuai() %%X%08.08X\n", status); /* turn off SYSPRV */ sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { if (DebugWatch) WatchThis (__LINE__, "sys$getuai() %%X%08.08X", status); return (FALSE); } /* automatically disallow if any of these flags are set! */ if (UaiFlags & DisallowVmsFlags) { if (DebugWatch) WatchThis (__LINE__, "FAILED on SYSUAF flags"); return (FALSE); } /* to upper case! (just truncate if too long) */ zptr = (sptr = Password) + sizeof(Password)-1; for (cptr = PasswordPtr; *cptr && sptr < zptr; *sptr++ = toupper(*cptr++)); *sptr = '\0'; PasswordDsc.dsc$w_length = sptr - Password; status = sys$hash_password (&PasswordDsc, UaiEncrypt, UaiSalt, &UserNameDsc, &HashedPwd); if (Debug) fprintf (stdout, "sys$hash_password() %%X%08.08X\n", status); if (VMSnok (status)) { if (DebugWatch) WatchThis (__LINE__, "sys$hash_password() %%X%08.08X", status); return (FALSE); } if (HashedPwd[0] != UaiPwd[0] || HashedPwd[1] != UaiPwd[1]) { if (DebugWatch) WatchThis (__LINE__, "FAILED password match"); return (FALSE); } if (DebugWatch) WatchThis (__LINE__, "VERIFIED"); return (TRUE); } /*****************************************************************************/ /* Roll-you-own here! */ BOOL VerifyLocalFunction ( char *StringPtr, char *PasswordPtr ) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VerifyLocalFunction() |%s|\n", StringPtr); return (FALSE); } /*****************************************************************************/ #if USE_OPENSSL /* Compare the supplied "{SHA}LcRNUdyib7gGaI8qq4wGNen46MoqL" SHA1 hash string to the supplied plain-text password. */ BOOL VerifySHA ( char *StringPtr, char *PasswordPtr ) { int len; char DecodeBuffer [20], PasswordHash [20]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VerifySHA() |%s|\n", StringPtr); if (!memcmp (StringPtr, "{SHA}", 5)) StringPtr += 5; len = DecodeBase64 (StringPtr, strlen(StringPtr), DecodeBuffer, sizeof(DecodeBuffer)); if (len != 20) return (FALSE); HashSHA1 (PasswordPtr, strlen(PasswordPtr), NULL, 0, PasswordHash); if (!memcmp (DecodeBuffer, PasswordHash, 20)) return (TRUE); return (FALSE); } /*****************************************************************************/ /* Compare the supplied "{SSHA}LcRNUdyib7gGaI8qq4wGNen46MoqLrPH" Salted SHA1 hash string to the supplied plain-text password. */ BOOL VerifySSHA ( char *StringPtr, char *PasswordPtr ) { int len; char DecodeBuffer [48], PasswordHash [20]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VerifySSHA() |%s|\n", StringPtr); if (!memcmp (StringPtr, "{SSHA}", 6)) StringPtr += 6; len = DecodeBase64 (StringPtr, strlen(StringPtr), DecodeBuffer, sizeof(DecodeBuffer)); if (len < 20 || len > 32) return (FALSE); HashSHA1 (PasswordPtr, strlen(PasswordPtr), DecodeBuffer+20, len-20, PasswordHash); if (!memcmp (DecodeBuffer, PasswordHash, 20)) return (TRUE); return (FALSE); } /*****************************************************************************/ /* Compare the supplied "{MD5}LcRNUdyib7gGaI8qq4wGNen46MoqL" MD5 hash string to the supplied plain-text password. */ BOOL VerifyMD5 ( char *StringPtr, char *PasswordPtr ) { int len; char DecodeBuffer [16], PasswordHash [16]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VerifyMD5() |%s|\n", StringPtr); if (!memcmp (StringPtr, "{MD5}", 5)) StringPtr += 5; len = DecodeBase64 (StringPtr, strlen(StringPtr), DecodeBuffer, sizeof(DecodeBuffer)); if (len != 16) return (FALSE); HashMD5 (PasswordPtr, strlen(PasswordPtr), NULL, 0, PasswordHash); if (!memcmp (DecodeBuffer, PasswordHash, 16)) return (TRUE); return (FALSE); } /*****************************************************************************/ /* Compare the supplied "{SMD5}LcRNUdy7gGaI8qq4wGNen46oqLrPm" Salted MD5 hash string to the supplied plain-text password. */ BOOL VerifySMD5 ( char *StringPtr, char *PasswordPtr ) { int len; char DecodeBuffer [48], PasswordHash [20]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VerifySMD5() |%s|\n", StringPtr); if (!memcmp (StringPtr, "{SMD5}", 6)) StringPtr += 6; len = DecodeBase64 (StringPtr, strlen(StringPtr), DecodeBuffer, sizeof(DecodeBuffer)); if (len < 16 || len > 32) return (FALSE); HashMD5 (PasswordPtr, strlen(PasswordPtr), DecodeBuffer+16, len-16, PasswordHash); if (!memcmp (DecodeBuffer, PasswordHash, 16)) return (TRUE); return (FALSE); } /*****************************************************************************/ /* Applies a (optionally Salted) SHA1 hash (RFC3112) to the supplied data. */ void HashSHA1 ( char *DataPtr, int DataLength, char *SaltPtr, int SaltLength, char *HashBuffer ) { SHA_CTX ctx; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HashSHA1()\n"); SHA1_Init (&ctx); SHA1_Update (&ctx, DataPtr, DataLength); if (SaltPtr) SHA1_Update (&ctx, SaltPtr, SaltLength); SHA1_Final ((unsigned char*)HashBuffer, &ctx); } /*****************************************************************************/ /* Applies a (optionally Salted) MD5 hash (RFC1321) to the supplied data. */ void HashMD5 ( char *DataPtr, int DataLength, char *SaltPtr, int SaltLength, char *HashBuffer ) { MD5_CTX ctx; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HashMD5()\n"); MD5_Init (&ctx); MD5_Update (&ctx, DataPtr, DataLength); if (SaltPtr) MD5_Update (&ctx, SaltPtr, SaltLength); MD5_Final ((unsigned char*)HashBuffer, &ctx); } /*****************************************************************************/ /* Using OpenSSL BIO streams decode base-64 into a plain-string. And yes, it does seem a bit of an overkill but as we're using OpenSSL for the SHA1 hashing why not just use as much more as we need! */ int DecodeBase64 ( char *Base64Ptr, int Base64Length, char *BufferPtr, int SizeOfBuffer ) { static BIO *bio, *BioB64Ptr, *BioMemPtr; int rc; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "DecodeBase64()\n"); if (!BioMemPtr) { BioMemPtr = BIO_new (BIO_s_mem()); if (!BioMemPtr) exit (SS$_BUGCHECK); BioB64Ptr = BIO_new (BIO_f_base64()); if (!BioB64Ptr) exit (SS$_BUGCHECK); BIO_set_flags (BioB64Ptr, BIO_FLAGS_BASE64_NO_NL); bio = BIO_push (BioB64Ptr, BioMemPtr); if (!bio) exit (SS$_BUGCHECK); } if (Base64Length == -1) Base64Length = strlen(Base64Ptr); BIO_reset (bio); rc = BIO_write (BioMemPtr, Base64Ptr, Base64Length); rc = BIO_flush (bio); rc = BIO_pending (bio); if (rc > SizeOfBuffer) rc = SizeOfBuffer; rc = BIO_read (bio, BufferPtr, rc); if (Debug) fprintf (stdout, "|%s|\n", BufferPtr); return (rc); } #endif /* USE_OPENSSL */ /*****************************************************************************/ /* Prevent the great unwashed from pillaging the treasures within! */ void NeedsPrivilegedAccount () { static unsigned long PrivAcctMask [2] = { PRV$M_SETPRV | PRV$M_SYSPRV, 0 }; static long Pid = -1; static unsigned long JpiAuthPriv [2]; static struct { unsigned short buf_len; unsigned short item; void *buf_addr; void *ret_len; } JpiItems [] = { { sizeof(JpiAuthPriv), JPI$_AUTHPRIV, &JpiAuthPriv, 0 }, {0,0,0,0} }; int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "NeedsPrivilegedAccount()\n"); status = sys$getjpiw (0, &Pid, 0, &JpiItems, 0, 0, 0); if (VMSnok (status)) exit (status); if (!(JpiAuthPriv[0] & PrivAcctMask[0])) exit (SS$_NOSYSPRV); } /*****************************************************************************/ /* Output and flush a record suitable for WASD to WATCH in callout mode. */ void WatchThis ( int SourceCodeLine, char *FormatString, ... ) { static unsigned long PrevBinTime [2]; static char TimeString [12]; static $DESCRIPTOR (TimeFaoDsc, "!2ZL:!2ZL:!2ZL.!2ZL\0"); static $DESCRIPTOR (TimeStringDsc, TimeString); int argcnt; unsigned long BinTime [2]; unsigned short NumTime [7]; va_list argptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WatchThis()\n"); sys$gettim (&BinTime); if (BinTime[0] != PrevBinTime[0] || BinTime[1] != PrevBinTime[1]) { PrevBinTime[0] == BinTime[0]; PrevBinTime[1] == BinTime[1]; sys$numtim (&NumTime, &BinTime); sys$fao (&TimeFaoDsc, NULL, &TimeStringDsc, NumTime[3], NumTime[4], NumTime[5], NumTime[6]); } fprintf (stdout, "000 [%d] %s ", SourceCodeLine, TimeString); va_count (argcnt); va_start (argptr, FormatString); vprintf (FormatString, argptr); fputs ("\n", stdout); fflush (stdout); } /*****************************************************************************/ /* Return a pointer to a string showing the elapsed time (for WATCHing). */ char* WatchElapsed () { static unsigned long StatTimerContext; static unsigned long StatTimerElapsedTime = 1; static char ElapsedTime [24]; static $DESCRIPTOR (ElapsedFaoDsc, "!%D\0"); static $DESCRIPTOR (ElapsedTimeDsc, ElapsedTime); int status; unsigned long ElapsedBinTime [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WatchElapsed()\n"); if (StatTimerContext) { status = lib$stat_timer (&StatTimerElapsedTime, &ElapsedBinTime, &StatTimerContext); if (VMSnok (status)) exit (status); sys$fao (&ElapsedFaoDsc, NULL, &ElapsedTimeDsc, &ElapsedBinTime); memmove (ElapsedTime, ElapsedTime+5, 12); status = lib$free_timer (&StatTimerContext); if (VMSnok (status)) exit (status); StatTimerContext = 0; return (ElapsedTime); } /* initialize statistics timer */ status = lib$init_timer (&StatTimerContext); if (VMSnok (status)) exit (status); return (NULL); } /*****************************************************************************/ /* Translate a logical name using LNM$FILE_DEV. Return a pointer to the value string, or NULL if the name does not exist. If 'LogValue' is supplied the logical name is translated into that (assumed to be large enough), otherwise it's translated into an internal static buffer. */ char* SysTrnLnm ( char *LogName, char *LogValue ) { static unsigned short ShortLength; static char StaticLogValue [256]; static $DESCRIPTOR (LogNameDsc, ""); static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { 255, LNM$_STRING, 0, &ShortLength }, { 0,0,0,0 } }; int status; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SysTrnLnm() |%s|\n", LogName); LogNameDsc.dsc$a_pointer = LogName; LogNameDsc.dsc$w_length = strlen(LogName); if (LogValue) cptr = LnmItems[0].buf_addr = LogValue; else cptr = LnmItems[0].buf_addr = StaticLogValue; status = sys$trnlnm (0, &LnmFileDevDsc, &LogNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (!(status & 1)) { if (Debug) fprintf (stdout, "|(null)|\n"); return (NULL); } cptr[ShortLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", cptr); return (cptr); } /*****************************************************************************/ /* Parses the string passed to it as a 'command-line' with parameters and qualifiers. Makes the real command-line and the authentication configuration parameters look and behave in the same fashion. */ void GetRunTimeParameters ( char *clptr, BOOL FromCli ) { char ch; char *aptr, *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "RunTimeParameters() |%s|\n", clptr); aptr = NULL; ch = *clptr; for (;;) { if (aptr && *aptr == '/') *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (FromCli && strsame (aptr, "/AUTH_PASSWORD=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (CliAuthPassword, cptr, sizeof(CliAuthPassword)); continue; } if (strsame (aptr, "/BASE=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (LdapBase, cptr, sizeof(LdapBase)); continue; } if (strsame (aptr, "/CERT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (CertKeyFileName, cptr, sizeof(CertKeyFileName)); continue; } if (strsame (aptr, "/DATT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (UserDisplayAttribute, cptr, sizeof(UserDisplayAttribute)); continue; } if (strsame (aptr, "/DIGEST", 4)) { VerifyDigest = TRUE; continue; } if (FromCli && strsame (aptr, "/DUMP", 4)) { DoDump = TRUE; continue; } if (FromCli && strsame (aptr, "/DBUG", -1)) { Debug = TRUE; continue; } if (strsame (aptr, "/FILTER=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (LdapFilter, cptr, sizeof(LdapFilter)); continue; } if (strsame (aptr, "/HOST=", 4) || strsame (aptr, "/SERVER=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (LdapHost, cptr, sizeof(LdapHost)); for (cptr = LdapHost; *cptr && *cptr != ':'; cptr++); if (*cptr) { *cptr++ = '\0'; if (isdigit(*cptr)) LdapPort = (short)atoi(cptr); } continue; } if (strsame (aptr, "/LOCAL", 4)) { VerifyLocal = TRUE; continue; } if (strsame (aptr, "/PATT=", 5)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (PasswordAttribute, cptr, sizeof(PasswordAttribute)); continue; } if (strsame (aptr, "/PORT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; LdapPort = (short)atoi(cptr); continue; } if (FromCli && strsame (aptr, "/REMOTE_USER=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (CliRemoteUser, cptr, sizeof(CliRemoteUser)); continue; } if (strsame (aptr, "/RESET", 4)) { DoDump = VerifySYSUAF = VerifyDigest = FALSE; LdapPort = LdapSSL = StartTLS = 0; LdapProtocolVersion = LDAP_VERSION3; CertKeyFileName[0] = CliAuthPassword[0] = CliRemoteUser[0] = LdapBase[0] = LdapFilter[0] = LdapHost[0] = LdapSimpleBindDn[0] = LdapSimpleBindPasswd[0] = PasswordAttribute[0] = UserDisplayAttribute[0] = UserNameDefault[0] = UserNameAttribute[0] = '\0'; strcpy (PasswordAttribute, DEFAULT_ATTRIBUTE_PASSWORD); strcpy (UserDisplayAttribute, DEFAULT_ATTRIBUTE_DISPLAY); strcpy (UserNameAttribute, DEFAULT_ATTRIBUTE_USERNAME); continue; } if (strsame (aptr, "/SIMPLE=", 4)) { LdapSimpleBind = TRUE; for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (LdapSimpleBindDn, cptr, sizeof(LdapSimpleBindDn)); for (cptr = LdapSimpleBindDn; *cptr && *cptr != ':'; cptr++) if (*cptr == '\\') RemoveSlash (cptr); if (*cptr) { *cptr++ = '\0'; strzcpy (LdapSimpleBindPasswd, cptr, sizeof(LdapSimpleBindPasswd)); for (cptr = LdapSimpleBindPasswd; *cptr && *cptr != ':'; cptr++) if (*cptr == '\\') RemoveSlash (cptr); } continue; } if (strsame (aptr, "/SSL=", 4)) { LdapSSL = 1; /* the default; TLSv1 */ for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; LdapSSL = atoi(cptr); continue; } if (strsame (aptr, "/TLS=", 4)) { StartTLS = 1; /* the default; TLSv1 */ for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; StartTLS = atoi(cptr); continue; } if (strsame (aptr, "/SYSUAF", 4)) { VerifySYSUAF = TRUE; continue; } if (strsame (aptr, "/UDEF=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (UserNameDefault, cptr, sizeof(UserNameDefault)); continue; } if (strsame (aptr, "/UATT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; strzcpy (UserNameAttribute, cptr, sizeof(UserNameAttribute)); continue; } if (strsame (aptr, "/VERSION=", 4)) { LdapProtocolVersion = LDAP_VERSION3; for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (!*cptr) continue; cptr++; LdapProtocolVersion = atoi(cptr); continue; } if (FromCli && strsame (aptr, "/WATCH", -1)) { DebugWatch = TRUE; continue; } if (FromCli) { if (*aptr == '/') { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); } else { fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } } } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line. */ void GetParameters () { static char CommandLine [512]; static unsigned long Flags = 0; int status; unsigned short Length; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetParameters()\n"); /* get the entire command line following the verb */ status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags); if (VMSnok (status)) exit (status); CommandLine[Length] = '\0'; GetRunTimeParameters (CommandLine, TRUE); } /****************************************************************************/ /* Remove the '\' for slash-escaped characters by shuffling all the remaining characters left. Elementary but enough for this. */ void RemoveSlash (char *cptr) { char *sptr; /*********/ /* begin */ /*********/ if (!*(sptr = cptr+1)) return; while (*sptr) *cptr++ = *sptr++; *cptr = '\0'; } /****************************************************************************/ /* Copy a string without overflow (or indication of it :-). */ int strzcpy ( char *String, char *cptr, int SizeOfString ) { char *sptr, *zptr; /*********/ /* begin */ /*********/ if (SizeOfString) SizeOfString--; zptr = (sptr = String) + SizeOfString; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; return (sptr - String); } /****************************************************************************/ /* Does a case-insensitive, character-by-character string compare and returns TRUE if two strings are the same, or FALSE if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ BOOL strsame ( char *sptr1, char *sptr2, int count ) { /*********/ /* begin */ /*********/ while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (FALSE); if (count) if (!--count) return (TRUE); } if (*sptr1 || *sptr2) return (FALSE); else return (TRUE); } /*****************************************************************************/