/* SiteMinder Login Form CGI Copyright (C) 2012 CA Technologies All rights reserved. CA Technologies makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. */ /* Sample CGI login application This CGI displays a form on a Get, collects posted data, and re-posts that data to the SiteMinder Forms Credential Collector (.fcc file). This application allows developers to display very dynamic login forms and to use the data posted by the user at login time before re-posting it to the SiteMinder FCC. It is recommended that you build this as a cgi application with a unique extension such as .smcgi, configure your web server to run cgi's with this extension, and then add this extension to your ignore url setting in your Web Agent configuration file. This will increase the overall performance of your login application. */ #include #include #ifdef AIX #include #else #include #endif #include #include "SmApi.h" #include "SmPasswordMsg.h" #define BUFSIZE 2048 #define lBUFSIZE 256 #ifdef unix #define stricmp strcasecmp #endif // Header needed for Form Get/Post #define pszHttpHeader "Status: 200 OK\nContent-Type: text/html\n\n\n" // Utility functions void ShowHTTPHeaderHTML(); void ShowErrHTML( const char* pszErr, const char* pszParam ); void ShowLoginForm( const char* pszTargetUrl); void PostToFcc( const char* pszUserName, const char* pszPassWord, const char* pszTargetUrl); int url_decode(char* target); char* GetQueryValue( char* pszQueryParamValue, const char* pszQuery, const char* pszQueryStringName ); int main(int argc, char** argv) { // NAMESPACE VARIABLES char* pszbuf = NULL; char* pszUserName = NULL; char* pszReason = NULL; char* pszTargetURL = NULL; char* pszUsrMsg = NULL; char* pszUsrMsgBuf = NULL; char* pszPassWord = NULL; char* pszQuery = NULL; char* pszCookies = NULL; char* pszMethod = NULL; char* pszContentLength = NULL; int nContentLength = 0; pszbuf = new char[BUFSIZE]; pszUserName = new char[BUFSIZE]; pszReason = new char[BUFSIZE]; pszTargetURL= new char[BUFSIZE]; pszUsrMsg = new char[BUFSIZE]; pszUsrMsgBuf = new char[BUFSIZE]; pszPassWord = new char[BUFSIZE]; pszQuery = new char[BUFSIZE]; pszCookies = new char[BUFSIZE]; pszMethod = new char[BUFSIZE]; pszContentLength = new char[BUFSIZE]; memset( (void *)pszbuf, 0, BUFSIZE); memset( (void *)pszUserName, 0, BUFSIZE); memset( (void *)pszReason, 0, BUFSIZE); memset( (void *)pszTargetURL, 0, BUFSIZE); memset( (void *)pszUsrMsg, 0, BUFSIZE); memset( (void *)pszUsrMsgBuf, 0, BUFSIZE); memset( (void *)pszPassWord, 0, BUFSIZE); memset( (void *)pszQuery, 0, BUFSIZE); memset( (void *)pszCookies, 0, BUFSIZE); memset( (void *)pszMethod, 0, BUFSIZE); memset( (void *)pszContentLength, 0, BUFSIZE ); // Start Forms Cred Display ShowHTTPHeaderHTML(); pszMethod = getenv( "REQUEST_METHOD" ); //ON LOAD from the HTTP Header variable extract HTTP METHOD //Depending on METHOD obtain User Credentials then place in pszQuery pszMethod = getenv( "REQUEST_METHOD" ); if ( NULL != pszMethod && NULL != strstr( pszMethod, "POST" ) ) { pszContentLength = getenv( "CONTENT_LENGTH" ); nContentLength = atoi( pszContentLength ); cin.getline( pszbuf, nContentLength + 1, '\n'); //Endl terminator not correct but ingnored if ( NULL != pszbuf ) strcpy ( pszQuery, pszbuf );// Ready to parse pszQuery as before // Get USERNAME, PASSWORD, and TARGET from post data // Get the target from the query string if( NULL != pszQuery ) { pszTargetURL = GetQueryValue( pszTargetURL, pszQuery, "TARGET=" ); pszUserName = GetQueryValue( pszUserName, pszQuery, "USERNAME=" ); pszPassWord = GetQueryValue( pszPassWord, pszQuery, "PASSWORD=" ); } else { ShowErrHTML("Missing Required Input Parameter(s).", NULL); return 1; } if ( NULL == pszTargetURL ) { ShowErrHTML("POST DATA is missing TARGET", NULL); return 1; } // INSERT CUSTOM CODE TO USE POSTED DATA HERE // Now decode URL url_decode(pszTargetURL); PostToFcc( pszUserName, pszPassWord, pszTargetURL ); return 0; } else if ( NULL != pszMethod && NULL != strstr( pszMethod, "GET" ) ) { // Extract the URL QUERY_STRING, COOKIES STRING pszQuery = getenv( "QUERY_STRING" ); // URL Parameters // Get the target from the query string if( NULL != pszQuery ) { pszTargetURL = GetQueryValue( pszTargetURL, pszQuery, "TARGET=" ); } else { ShowErrHTML("Missing Required Input Parameter(s).", NULL); return 1; } if ( NULL == pszTargetURL ) { ShowErrHTML("QUERY_STRING is missing TARGET", NULL); return 1; } ShowLoginForm(pszTargetURL); return 0; } else // not GET or POST { ShowErrHTML("Bad Request Method.", NULL); return 1; } if( NULL != pszQuery ) { //Collect the Redirect Parameters from the QUERY_STRING pszUserName = GetQueryValue( pszUserName, pszQuery, "USERNAME=" ); pszPassWord = GetQueryValue( pszPassWord, pszQuery, "PASSWORD=" ); pszTargetURL = GetQueryValue( pszTargetURL, pszQuery, "TARGET=" ); } else { ShowErrHTML("Missing Required Input Parameters.", NULL); return 1; } // Check for Critical Missing Parameters if ( NULL == pszTargetURL ) { ShowErrHTML("QUERY_STRING is missing TARGET", NULL); return 1; } } // end main // Output of form that will re-post data to SiteMinder FCC void PostToFcc (const char* pszUserName, const char* pszPassWord, const char* pszTargetURL) { cout << "\n"; cout << "SiteMinder Password Services\n"; cout << "\n"; // Javascript for automatic submitting of form, posting data to fcc cout << "\n"; cout << "\n"; cout << "\n"; cout << "\n"; cout << "
\n"; cout << "\n"; cout << "
"; if( NULL != pszUserName) { cout << "\n"; } if( NULL != pszPassWord ) { cout << "\n"; } if( NULL != pszTargetURL ) { cout << "\n"; } // Just in case java script is turned off cout << "
\n"; cout << "
\n"; cout << endl << flush; } // Output of HTML for Login Form Page // If your CGI is only being used to display dynamic content in your login form, // then you can have your form post data directly to the SiteMinder .fcc target. void ShowLoginForm ( const char* pszTargetURL ) { cout << "\n"; cout << "Sample Login Form\n"; cout << "\n"; cout << "

Please enter your login credentials

\n"; cout << "
\n"; cout << "\n"; cout << " \n"; cout << " \n"; cout << "\n"; cout << "\n"; cout << " \n"; cout << " \n"; cout << "\n"; cout << "\n"; cout << "\n"; cout << "
User Name:
Password:
\n"; cout << "\n"; cout << endl << flush; } // Output of HTML for Error Page void ShowErrHTML( const char* pszErrMsg, const char* pszParam ) { cout << "Diagnostic Information\n"; cout << "\n"; cout << "\n"; cout << "
  \"Logo\"
\n"; cout << "
\n"; cout << "
\n"; cout << "
\n"; cout << "\n"; cout << "
\n"; cout << "\n"; cout << "\n"; cout << "\n"; cout << "\n"; cout << "\n"; cout << "\n"; cout << "\n"; cout << "
\n"; cout << "Diagnostic Information
Security Protection Fault:\n"; if (pszParam != NULL) { cout << pszErrMsg << pszParam; } else { cout << pszErrMsg; } cout << "
\n"; cout << "Please contact your Security Administrator or Help Desk.
\n"; cout << endl << flush; } char *GetQueryValue( char* pszQueryParamValue, const char* pszQuery, const char* pszQueryStringName ) { char szTemp[BUFSIZE]; char* pszBuf; char* pszBufValue; pszBuf = new char[BUFSIZE]; pszBufValue = new char[BUFSIZE]; memset( (void *)pszQueryParamValue, 0, BUFSIZE ); memset( (void *)pszBuf, 0, BUFSIZE ); memset( (void *)pszBufValue, 0, BUFSIZE ); memset( (void *)szTemp, 0, BUFSIZE ); strcpy ( szTemp, pszQuery ); pszBuf = strstr( szTemp, pszQueryStringName ); if ( NULL == pszBuf ){ pszQueryParamValue = NULL; return ( pszQueryParamValue ); } else { strtok( pszBuf, "&" ); strtok( pszBuf, "=" ); pszBufValue = strtok(NULL, "&" ); if ( pszBufValue == NULL ){ pszQueryParamValue = NULL; return ( pszQueryParamValue ); } else { strcpy( pszQueryParamValue, pszBufValue ); return ( pszQueryParamValue ); //SUCCESS } } } void ShowHTTPHeaderHTML() { cout << pszHttpHeader; } /* This function replaces a string with the URL-decoded version of that string. Since URL-decoding leaves the string the same length or shortens it, this is safe. It returns the new length. */ int url_decode(char *target) { char *temp; /* copy of target */ char c1, c2; /* holders for hex digits of escape sequence */ int len; /* length of original string */ int k; /* index for writing to decoded string */ int j; /* index for reading from encoded string */ /* First, we make a copy of the target string to read from while we're writing back into the original. If I had more guts, I could probably have done it all in one string, since the length of the new string is always less or equal to that of the original and thus never overwrites characters we haven't yet examined, but I felt it was better to be simple and straightforward than clever. */ len = strlen(target); temp = (char*)malloc(sizeof(char) * (len + 1)); strcpy(temp, target); /* Run down the length of the encoded string, examining each character. If it's a +, we write a space to the decoded string. If it's a %, we discard it, read in the next two characters, convert their hex value to a char, and write that. Anything else, we just copy over. */ for ( j=0, k=0; j < len; j++, k++ ) { switch( temp[j] ) { case '+': target[k] = ' '; break; case '%': c1 = tolower(temp[++j]); if ( isdigit(c1) ) { c1 -= '0'; } else { c1 = c1 - 'a' + 10; } c2 = tolower(temp[++j]); if ( isdigit(c2) ) { c2 -= '0'; } else { c2 = c2 - 'a' + 10; } target[k] = c1 * 16 + c2; break; default: target[k] = temp[j]; break; } } target[k] = '\0'; free(temp); return(k); }