blob: 62f6d9255f5edaa58b9f1c504063b4d11b1d4f18 [file]
//******************************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//
// @@@ END COPYRIGHT @@@
//******************************************************************************
//******************************************************************************
// *
// This program is used to test authenticating with an LDAP server *
// configured in a Trafodion instance. It may also be used to test the *
// correctness of the code that communicates with the LDAP server. *
// *
// Trafodion does not to be started to run this program. *
// *
//******************************************************************************
#include "ldapconfignode.h"
#include "authEvents.h"
#include "auth.h"
#include <stdio.h>
#include <string.h>
#include <string>
#include <stdlib.h>
#include <iostream>
#include <unistd.h>
#include <getopt.h>
#include <termios.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <vector>
using namespace std;
enum Operation {
Authenticate = 2,
Lookup = 3
};
AUTH_OUTCOME authenticateLDAPUser(
std::vector<AuthEvent> & authEvents,
const char * username,
const char * password,
LDAPConfigNode::LDAPConfigType configType,
char * searchHostName,
char * authHostName);
void doAuthenticate(
std::vector<AuthEvent> & authEvents,
const char * username,
const char * password,
LDAPConfigNode::LDAPConfigType configType,
bool verbose);
void doCanaryCheck(
std::vector<AuthEvent> & authEvents,
const char * username,
LDAPConfigNode::LDAPConfigType configType,
bool verbose,
AUTH_OUTCOME exitCode);
LDSearchStatus lookupLDAPUser(
std::vector<AuthEvent> & authEvents,
const char * username,
LDAPConfigNode::LDAPConfigType configType,
char * searchHostName);
void printTime();
void reportResults(
Operation operation,
const std::vector<AuthEvent> & authEvents,
const char * username,
AUTH_OUTCOME outcome,
int verbose);
void setEcho(bool enable);
// *****************************************************************************
// * *
// * Function: authenticateLDAPUser *
// * *
// * Authenticate an LDAP user with either the primary or secondary LDAP *
// * configuration. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <username> const char * In *
// * is the external username to be checked on the directory server. *
// * *
// * <password> const char * In *
// * is the password of the external user *
// * *
// * <configType> LDAPConfigNode::LDAPConfigType In *
// * is the type of configuration to use. *
// * *
// *****************************************************************************
// * *
// * Returns: AUTH_OUTCOME *
// * *
// * AUTH_OK -- User was authenticated on LDAP server *
// * AUTH_REJECTED -- User was rejected by LDAP server *
// * AUTH_FAILED -- An error prevented authentication *
// * *
// *****************************************************************************
AUTH_OUTCOME authenticateLDAPUser(
std::vector<AuthEvent> & authEvents,
const char * username,
const char * password,
LDAPConfigNode::LDAPConfigType configType,
char * searchHostName,
char * authHostName)
{
string userDN = ""; // User DN, used to bind to LDAP serer
LDAPConfigNode *searchNode;
// Zero len password is a non-auth bind in LDAP, so we treat it
// as a failed authorization.
if (strlen(password) == 0)
return AUTH_REJECTED;
searchNode = LDAPConfigNode::GetLDAPConnection(authEvents,configType,SearchConnection,
searchHostName);
if (searchNode == NULL)
return AUTH_FAILED;
LDSearchStatus searchStatus = searchNode->lookupUser(authEvents,username,userDN);
if (searchStatus == LDSearchNotFound)
return AUTH_REJECTED;
if (searchStatus != LDSearchFound)
return AUTH_FAILED;
// User is defined there. But is their password correct?
LDAPConfigNode *authNode = LDAPConfigNode::GetLDAPConnection(authEvents,configType,
AuthenticationConnection,
authHostName);
if (authNode == NULL)
return AUTH_FAILED;
// User exists, let's validate that non-blank password!
LDAuthStatus authStatus = authNode->authenticateUser(authEvents,userDN.c_str(),password);
if (authStatus == LDAuthSuccessful)
return AUTH_OK;
if (authStatus == LDAuthRejected)
return AUTH_REJECTED;
return AUTH_FAILED;
}
//************************ End of authenticateLDAPUser *************************
// *****************************************************************************
// * *
// * Function: doAuthenticate *
// * *
// * Authenticate an LDAP user with either the primary or secondary LDAP *
// * configuration. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <username> const char * In *
// * is the external username to be checked on the directory server. *
// * *
// * <password> const char * In *
// * is the password of the external user *
// * *
// * <configType> LDAPConfigNode::LDAPConfigType In *
// * is the type of configuration to use. *
// * *
// * <verbose> bool In *
// * if true, additional output is produced. *
// * *
// *****************************************************************************
void doAuthenticate(
std::vector<AuthEvent> & authEvents,
const char * username,
const char * password,
LDAPConfigNode::LDAPConfigType configType,
bool verbose)
{
LDAPConfigNode::ClearRetryCounts();
char searchHostName[256];
char authHostName[256];
searchHostName[0] = authHostName[0] = 0;
AUTH_OUTCOME rc = authenticateLDAPUser(authEvents,username,password,configType,
searchHostName,authHostName);
if (verbose)
{
cout << "Search host name: " << searchHostName << endl;
cout << "Auth host name: " << authHostName << endl;
}
// How'd it go? In addition to a thumbs up/down, print diagnostics if
// requested. This matches production behavior; we log retries if
// successful, and we log all internal errors as well as number of retries
// if authentication failed due to a resource error. Resource errors include
// problems with the LDAP servers and parsing the LDAP connection configuration
// file (.traf_authentication_config).
reportResults(Authenticate,authEvents,username,rc,verbose);
}
//*************************** End of doAuthenticate ****************************
// *****************************************************************************
// * *
// * Function: doCanaryCheck *
// * *
// * Lookup a user on an LDAP server to test configuration and connection. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <username> const char * In *
// * is the external username to be checked on the directory server. *
// * *
// * <configType> LDAPConfigNode::LDAPConfigType In *
// * is the type of configuration to use. *
// * *
// * <verbose> bool In *
// * if true, additional output is produced. *
// * *
// *****************************************************************************
void doCanaryCheck(
std::vector<AuthEvent> & authEvents,
const char * username,
LDAPConfigNode::LDAPConfigType configType,
bool verbose,
AUTH_OUTCOME exitCode)
{
exitCode = AUTH_OK;
char searchHostName[256];
searchHostName[0] = 0;
LDSearchStatus searchStatus = lookupLDAPUser(authEvents,username,configType,searchHostName);
if (verbose)
cout << "Search host name: " << searchHostName << endl;
switch (searchStatus)
{
case LDSearchFound:
exitCode = AUTH_OK;
break;
case LDSearchNotFound:
exitCode = AUTH_REJECTED;
break;
case LDSearchResourceFailure:
exitCode = AUTH_FAILED;
break;
default:
exitCode = AUTH_FAILED;
}
reportResults(Lookup,authEvents,username,exitCode,verbose);
}
//*************************** End of doCanaryCheck *****************************
// *****************************************************************************
// * *
// * Function: lookupLDAPUser *
// * *
// * Determines if the username is defined on an LDAP server in the *
// * specified configuration. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <username> const char * In *
// * is the external username to be checked on the directory server. *
// * *
// * <configType> LDAPConfigType In *
// * specifies which configuration (i.e.,which set of LDAP servers) to use *
// * when searching for the username. *
// * *
// *****************************************************************************
// * *
// * Returns: LDSearchStatus *
// * *
// * LDSearchFound -- User was found on LDAP server *
// * LDSearchNotFound -- User was not found on LDAP server *
// * LDSearchResourceFailure -- An error prevented checking *
// * *
// *****************************************************************************
LDSearchStatus lookupLDAPUser(
std::vector<AuthEvent> & authEvents,
const char * username,
LDAPConfigNode::LDAPConfigType configType,
char * searchHostName)
{
LDAPConfigNode *searchNode;
searchNode = LDAPConfigNode::GetLDAPConnection(authEvents,configType,SearchConnection,
searchHostName);
if (searchNode == NULL)
return LDSearchResourceFailure;
string userDN = "";
return searchNode->lookupUser(authEvents,username,userDN);
}
//************************** End of lookupLDAPUser *****************************
// *****************************************************************************
// * *
// * Function: printTime *
// * *
// * Displays the current time in format YYYY-MM-DD hh:mm:ss. *
// * *
// *****************************************************************************
void printTime()
{
time_t t = time(NULL);
struct tm tm = *localtime(&t);
printf("%4d-%02d-%02d %02d:%02d:%02d\n",tm.tm_year + 1900,tm.tm_mon + 1,
tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
}
//***************************** End of printTime *******************************
// *****************************************************************************
// * *
// * Function: printUsage *
// * *
// * Displays usage information for this program. *
// * *
// *****************************************************************************
void printUsage()
{
cout << "Usage:ldapcheck [option]..." << endl;
cout << "option ::= --help|-h display usage information" << endl;
cout << " --username=<LDAP-username>" << endl;
cout << " --password[=<password>]" << endl;
cout << " --platform Use internal cluster configuration" << endl;
cout << " --primary Use primary (ENTERPRISE|LOCAL) configuration" << endl;
cout << " --secondary Use secondary (CLUSTER|REMOTE) configuration" << endl;
cout << " --verbose Display non-zero retry counts and LDAP errors" << endl;
}
//***************************** End of printUsage ******************************
// *****************************************************************************
// * *
// * Function: reportResults *
// * *
// * Logs and optionally displays any retries, errors, and outcome of an *
// * authentication or lookup operation. *
// * *
// *****************************************************************************
void reportResults(
Operation operation,
const std::vector<AuthEvent> & authEvents,
const char * username,
AUTH_OUTCOME outcome,
int verbose)
{
std::string msg;
// Report any retries
size_t bindRetryCount = LDAPConfigNode::GetBindRetryCount();
size_t searchRetryCount = LDAPConfigNode::GetSearchRetryCount();
char operationString[20];
if (operation == Authenticate)
strcpy(operationString,"Authentication");
else
strcpy(operationString,"Lookup");
// Log and optionally display event for:
// search (name lookup) operation that was retried
if (searchRetryCount > 0)
{
char searchRetryMessage[5100];
sprintf(searchRetryMessage,"%s required %d search retries. ",
operationString,searchRetryCount);
msg = searchRetryMessage;
AuthEvent authEvent (DBS_AUTH_RETRY_SEARCH, msg, LL_INFO);
authEvent.setCallerName("ldapcheck");
authEvent.logAuthEvent();
if (verbose)
cout << searchRetryMessage << endl;
}
// Log and optionally display event for:
// any bind (password authentication) operation that was retried
if (bindRetryCount > 0)
{
char bindRetryMessage[5100];
sprintf(bindRetryMessage,"%s required %d bind retries. ",
operationString,bindRetryCount);
msg = bindRetryMessage;
AuthEvent authEvent (DBS_AUTH_RETRY_BIND, msg, LL_INFO);
authEvent.setCallerName("ldapcheck");
authEvent.logAuthEvent();
if (verbose)
cout << bindRetryMessage << endl;
}
// report any errors and retries
// Walk the list of errors encountered during the attempt to authenticate
// the username, or username and password, and for each error, log the event
// ID and text. If verbose is true and logLevel is above the error
// level, send message to standard out.
std::string callerName ("ldapcheck");
for (size_t i = 0; i < authEvents.size(); i++)
{
AuthEvent authEvent = authEvents[i];
authEvent.setCallerName(callerName);
authEvent.logAuthEvent();
if (verbose)
cout << "ERROR: " << authEvent.getEventText().c_str() << endl;
}
// Finally, report the outcome.
std::string outcomeDesc = getAuthOutcome(outcome);
char buf[MAX_EVENT_MSG_SIZE];
snprintf(buf, MAX_EVENT_MSG_SIZE,
"Authentication request: externalUser %s, "
"result %d (%s)",
username,
(int)outcome, outcomeDesc.c_str());
msg = buf;
AuthEvent authEvent (DBS_AUTHENTICATION_ATTEMPT,msg, LL_INFO);
authEvent.setCallerName("ldapcheck");
authEvent.logAuthEvent();
cout << "INFO: " << authEvent.getEventText().c_str() << endl;
}
//*************************** End of reportResults *****************************
// *****************************************************************************
// * *
// * Function: setEcho *
// * *
// * Enables or disables echoing of input characters. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <enable> bool In *
// * is true if echo should be enabled, false if it should be disabled. *
// * *
// *****************************************************************************
void setEcho(bool enable)
{
termios tty;
tcgetattr(STDIN_FILENO,&tty);
if (enable)
tty.c_lflag |= ECHO;
else
tty.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO,TCSANOW,&tty);
}
//****************************** End of setEcho ********************************
#pragma page "main"
// *****************************************************************************
// * *
// * Function: main *
// * *
// * Parses the arguments and attempt to authentication the username and *
// * password. *
// * *
// * Trafodion does not to be started to run this program. *
// * *
// *****************************************************************************
int main(int argc,char *argv[])
{
// ldapcheck needs a username. If not supplied, issue an error
// and print usage information.
if (argc <= 1)
{
cout << "Username required to check LDAP" << endl;
printUsage();
exit(1);
}
// Help!
if (strcmp(argv[1],"-h") == 0 || strcmp(argv[1],"--help") == 0)
{
printUsage();
exit(0);
}
enum Options {
Primary = 1,
Secondary = 0,
Platform = 2};
int c;
int optionFlag = Primary;
int verbose = 0;
string password;
char username[129];
bool usernameSpecified = false;
bool passwordSpecified = false;
int loopCount = 1;
int delayTime = 0;
bool looping = false;
// Walk the list of options. Username and password are required, although
// the password can be left blank and prompted for.
while (true)
{
static struct option long_options[] =
{
{"one", no_argument,&optionFlag,Primary},
{"two", no_argument,&optionFlag,Secondary},
{"primary", no_argument,&optionFlag,Primary},
{"secondary", no_argument,&optionFlag,Secondary},
{"enterprise",no_argument,&optionFlag,Primary},
{"cluster", no_argument,&optionFlag,Secondary},
{"local", no_argument,&optionFlag,Primary},
{"remote", no_argument,&optionFlag,Secondary},
{"platform", no_argument,&optionFlag,Platform},
{"verbose", no_argument,&verbose,1},
{"loop", optional_argument,0,'l'},
{"delay", optional_argument,0,'d'},
{"password", optional_argument,0,'p'},
{"username", required_argument,0,'u'},
{0, 0, 0, 0}
};
int option_index = 0;
c = getopt_long(argc,argv,"u:p::",long_options,&option_index);
// No more options, break out of loop
if (c == -1)
break;
switch (c)
{
case 0:
break;
case 'u':
if (usernameSpecified)
{
cout << "Username already specified" << endl;
exit(1);
}
if (strlen(optarg) >= sizeof(username))
{
cout << "Username limited to 128 characters" << endl;
exit(1);
}
strcpy(username,optarg);
usernameSpecified = true;
break;
case 'p':
if (passwordSpecified)
{
cout << "Password already specified" << endl;
exit(1);
}
if (optarg)
{
if (strlen(optarg) > 64)
{
cout << "Password limited to 64 characters" << endl;
exit(1);
}
password = optarg;
}
else
do
{
cout << "Password: ";
setEcho(false);
getline(cin,password);
setEcho(true);
cout << endl;
if (password.size() > 64)
cout << "Password limited to 64 characters" << endl;
}
while (password.size() > 64);
passwordSpecified = true;
break;
case 'l':
loopCount = atoi(optarg);
if (loopCount <= 0)
{
cout << "Loop count must be at least 1" << endl;
exit(1);
}
looping = true;
case 'd':
delayTime = atoi(optarg);
if (delayTime <= 0)
{
cout << "Delay time must be at least 1" << endl;
exit(1);
}
case '?':
// getopt_long already printed an error message.
break;
default:
cout << "Internal error,option " << c << endl;
exit(1);
}
}
// If there are any remaining command line arguments, report an error
if (optind < argc)
{
cout << "Unrecognized text" << endl;
while (optind < argc)
cout << argv[optind++] << " ";
cout << endl;
printUsage();
exit(1);
}
// Verify a username was supplied, or we have nothing to do
if (!usernameSpecified)
{
cout << "Username required" << endl;
printUsage();
exit(1);
}
LDAPConfigNode::LDAPConfigType configType = LDAPConfigNode::PrimaryConfiguration;
switch (optionFlag)
{
case Secondary:
configType = LDAPConfigNode::SecondaryConfiguration;
break;
case Primary:
break;
configType = LDAPConfigNode::PrimaryConfiguration;
case Platform:
break;
configType = LDAPConfigNode::SecondaryInternalConfiguration;
break;
default: //Should not happen, but just in case
configType = LDAPConfigNode::PrimaryConfiguration;
}
// allocate authEvents to store any issues
std::vector<AuthEvent> authEvents;
authInitEventLog();
// If no password is supplied, we just perform a name lookup. This was
// added to provide a canary check for the LDAP server without having to
// supply a valid password.
if (!passwordSpecified)
{
AUTH_OUTCOME exitCode = AUTH_OK;
while (loopCount--)
{
if (verbose && looping)
printTime();
doCanaryCheck(authEvents,username,configType,verbose,exitCode);
if (loopCount > 0)
sleep(delayTime);
}
// For the LDAP Canary check mode of ldapcheck, we return one of
// three exit codes (AUTH_OUTCOME)
//
// AUTH_OK: LDAP configuration and server(s) good
// AUTH_REJECTED: User was not defined in LDAP
// AUTH_FAILED: Could not communicate with LDAP server(s)
// (check LDAP configuration or server(s))
exit((int)exitCode);
}
// We have a username and password. Let's authenticate!
while (loopCount--)
{
if (verbose && looping)
printTime();
doAuthenticate(authEvents,username,password.c_str(),configType,verbose);
if (loopCount > 0)
sleep(delayTime);
}
exit(0);
}
//******************************** End of main *********************************