-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Client Certificate Authentication with R_usermap #371
Changes from 4 commits
c0da63b
ee4c918
0d82a1c
31fe0d3
1c66746
4380270
28accac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2653,19 +2653,23 @@ static int safAuthenticate(HttpService *service, HttpRequest *request, AuthRespo | |
} else if (authDataFound){ | ||
ACEE *acee = NULL; | ||
strupcase(request->username); /* upfold username */ | ||
if (request->password == NULL) { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "Password is null. Calling safAuthenticate without a password.\n"); | ||
} else { | ||
#ifdef ENABLE_DANGEROUS_AUTH_TRACING | ||
#ifdef METTLE | ||
printf("SAF auth for user: '%s'\n", request->username); | ||
printf("SAF auth for user: '%s'\n", request->username); | ||
#else | ||
printf("u: '%s' p: '%s'\n",request->username,request->password); | ||
printf("u: '%s' p: '%s'\n",request->username,request->password); | ||
#endif | ||
#endif | ||
if (isLowerCasePasswordAllowed() || isPassPhrase(request->password)) { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "mixed-case system or a pass phrase, not upfolding password\n"); | ||
/* don't upfold password */ | ||
} else { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "non-mixed-case system, not a pass phrase, upfolding password\n"); | ||
strupcase(request->password); /* upfold password */ | ||
if (isLowerCasePasswordAllowed() || isPassPhrase(request->password)) { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "mixed-case system or a pass phrase, not upfolding password\n"); | ||
/* don't upfold password */ | ||
} else { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "non-mixed-case system, not a pass phrase, upfolding password\n"); | ||
strupcase(request->password); /* upfold password */ | ||
} | ||
Comment on lines
+2661
to
+2672
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need these white space changes? |
||
} | ||
|
||
#if APF_AUTHORIZED | ||
|
@@ -2675,10 +2679,17 @@ static int safAuthenticate(HttpService *service, HttpRequest *request, AuthRespo | |
|
||
CrossMemoryServerName *privilegedServerName = getConfiguredProperty(service->server, HTTP_SERVER_PRIVILEGED_SERVER_PROPERTY); | ||
int pwdCheckRC = 0, pwdCheckRSN = 0; | ||
pwdCheckRC = zisCheckUsernameAndPassword(privilegedServerName, | ||
request->username, request->password, &status); | ||
authResponse->type = AUTH_TYPE_RACF; | ||
authResponse->responseDetails.safStatus = status.safStatus; | ||
if (request->password != NULL) { | ||
pwdCheckRC = zisCheckUsernameAndPassword(privilegedServerName, | ||
request->username, request->password, &status); | ||
authResponse->type = AUTH_TYPE_RACF; | ||
authResponse->responseDetails.safStatus = status.safStatus; | ||
} else { | ||
pwdCheckRC = zisCheckUsername(privilegedServerName, | ||
request->username, &status); | ||
authResponse->type = AUTH_TYPE_RACF; | ||
authResponse->responseDetails.safStatus = status.safStatus; | ||
} | ||
Comment on lines
+2687
to
+2692
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need more info about this and why we need this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Irek, we need either a new API, or a new arg in the existing API to say if password is required. |
||
|
||
if (pwdCheckRC != 0) { | ||
#ifdef DEBUG_AUTH | ||
|
@@ -3142,7 +3153,7 @@ static int serviceAuthNativeWithSessionToken(HttpService *service, HttpRequest * | |
int authDataFound = FALSE; | ||
HttpHeader *authenticationHeader = getHeader(request,"Authorization"); | ||
char *tokenCookieText = getCookieValue(request,getSessionTokenCookieName(service)); | ||
|
||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, | ||
"serviceAuthNativeWithSessionToken: authenticationHeader 0x%p\n", | ||
"extractFunction 0x%p\n", | ||
|
@@ -3162,9 +3173,51 @@ static int serviceAuthNativeWithSessionToken(HttpService *service, HttpRequest * | |
if (service->authExtractionFunction(service, request) == 0){ | ||
authDataFound = TRUE; | ||
} | ||
} | ||
} | ||
|
||
/* Doubtful that it would be greater than 8k... */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we know for sure? I.e. references to standards/docs would help here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check the R_usermap doc for limits. Some 'net searching yielded 8K, blame me for this. |
||
|
||
#define TLS_CLIENT_CERTIFICATE_MAX_LENGTH 8000 | ||
|
||
char clientCertificate[TLS_CLIENT_CERTIFICATE_MAX_LENGTH] = {0}; | ||
unsigned int clientCertificateLength = 0; | ||
|
||
int rc = getClientCertificate(response->socket->tlsSocket->socketHandle, clientCertificate, sizeof(clientCertificate), &clientCertificateLength); | ||
if (rc != 0) { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG, "getClientCertificate - %d.\n", rc); | ||
} | ||
|
||
#ifdef ENABLE_DANGEROUS_AUTH_TRACING | ||
/* We probably don't want to dump their certificate, right? */ | ||
dumpbuffer(clientCertificate, clientCertificateLength); | ||
#endif | ||
|
||
if (rc == 0 && clientCertificateLength > 0) { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG, "There is a client certificate attached to the request.\n"); | ||
/* | ||
* We don't want to do this if we already found authentication data. | ||
*/ | ||
if (authDataFound == FALSE) { | ||
#define TLS_USERID_LENGTH 9 | ||
char userid[TLS_USERID_LENGTH] = {0}; | ||
int racfReturnCode = 0, racfReasonCode = 0; | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_INFO, "There was no token or credentials found in the request. Server is attempting to map the client certificate.\n"); | ||
int safReturnCode = getUseridByCertificate(clientCertificate, clientCertificateLength, userid, &racfReturnCode, &racfReasonCode); | ||
if (safReturnCode == 0) { | ||
request->username = userid; | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_INFO, "Found user '%s' from client certificate.\n", request->username); | ||
request->password = NULL; | ||
// null password with a valid user tells the server we authenticated with a certificate | ||
authDataFound = TRUE; | ||
} else { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_INFO, "No user was found for client certificate. (rc = 0x%x racfRC = 0x%x racfRSN = 0x%x\n", safReturnCode, racfReturnCode, racfReasonCode); | ||
} | ||
} else { | ||
zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_INFO, "Client certificate was attached to request, but credentials are also attached. Server won't attempt to map the client certificate.\n"); | ||
} | ||
} | ||
|
||
response->sessionCookie = NULL; | ||
|
||
AUTH_TRACE("AUTH: tokenCookieText: %s\n",(tokenCookieText ? tokenCookieText : "<noAuthToken>")); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,40 @@ | |
#include "fdpoll.h" | ||
#include "tls.h" | ||
|
||
int getClientCertificate(gsk_handle soc_handle, char *clientCertificate, unsigned int clientCertificateBufferSize, unsigned int *clientCertificateLength) { | ||
|
||
if (clientCertificate == NULL || clientCertificateBufferSize <= 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: |
||
return -1; | ||
} | ||
|
||
memset(clientCertificate, 0, clientCertificateBufferSize); | ||
*clientCertificateLength = 0; | ||
|
||
gsk_cert_data_elem *gskCertificateArray = NULL; | ||
int gskCertificateArrayElementCount = 0; | ||
|
||
int rc = gsk_attribute_get_cert_info(soc_handle, GSK_PARTNER_CERT_INFO, &gskCertificateArray, &gskCertificateArrayElementCount); | ||
|
||
if (rc != 0) { | ||
return rc; | ||
} | ||
|
||
gsk_cert_data_elem *tmp = gskCertificateArray; | ||
|
||
for (int i = 0; i++ < gskCertificateArrayElementCount; tmp++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not increment in a condition; this seems error prone. What about this? for (int i = 0; i < gskCertificateArrayElementCount; i++) {
const gsk_cert_data_elem *elem = &gskCertificateArray[i];
. . .
} |
||
if (tmp->cert_data_id == CERT_BODY_DER) { | ||
if (clientCertificateBufferSize >= tmp->cert_data_l) { | ||
memcpy(clientCertificate, tmp->cert_data_p, tmp->cert_data_l); | ||
*clientCertificateLength = tmp->cert_data_l; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point we've copied the data and continue. Is it okay to overwrite it if one of the next array entries matches the condition or do we need to break here? |
||
} | ||
} | ||
} | ||
|
||
gsk_free_cert_data(gskCertificateArray, gskCertificateArrayElementCount); | ||
|
||
return 0; | ||
} | ||
|
||
int tlsInit(TlsEnvironment **outEnv, TlsSettings *settings) { | ||
int rc = 0; | ||
TlsEnvironment *env = (TlsEnvironment *)safeMalloc(sizeof(*env), "Tls Environment"); | ||
|
@@ -29,6 +63,23 @@ int tlsInit(TlsEnvironment **outEnv, TlsSettings *settings) { | |
rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_PROTOCOL_TLSV1_1, GSK_PROTOCOL_TLSV1_1_OFF); | ||
rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_PROTOCOL_TLSV1_2, GSK_PROTOCOL_TLSV1_2_ON); | ||
rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_SERVER_EPHEMERAL_DH_GROUP_SIZE, GSK_SERVER_EPHEMERAL_DH_GROUP_SIZE_2048); | ||
|
||
/* | ||
* Don't validate certificates, maybe put behind a dangerous ifdef. | ||
* | ||
* rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_CLIENT_AUTH_TYPE, GSK_CLIENT_AUTH_PASSTHRU_TYPE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dead code? |
||
*/ | ||
|
||
#ifdef DEV_DO_NOT_VALIDATE_CLIENT_CERTIFICATES | ||
rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_CLIENT_AUTH_TYPE, GSK_CLIENT_AUTH_PASSTHRU_TYPE); | ||
#endif | ||
|
||
/* | ||
* Only allow requests with client certificates, maybe put behind a different ifdef. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dead code? |
||
* | ||
* rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_CLIENT_AUTH_ALERT, GSK_CLIENT_AUTH_NOCERT_ALERT_ON); | ||
*/ | ||
|
||
rc = rc || gsk_attribute_set_buffer(env->envHandle, GSK_KEYRING_FILE, settings->keyring, 0); | ||
if (settings->stash) { | ||
rc = rc || gsk_attribute_set_buffer(env->envHandle, GSK_KEYRING_STASH_FILE, settings->stash, 0); | ||
|
@@ -43,13 +94,15 @@ int tlsInit(TlsEnvironment **outEnv, TlsSettings *settings) { | |
safeFree((char*)env, sizeof(*env)); | ||
*outEnv = NULL; | ||
} | ||
//printf("tlsInit - rc=%d\n", rc); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please clean up dead code. |
||
return rc; | ||
} | ||
|
||
int tlsDestroy(TlsEnvironment *env) { | ||
int rc = 0; | ||
rc = gsk_environment_close(env->envHandle); | ||
safeFree((char*)env, sizeof(*env)); | ||
//printf("tlsDestroy - rc=%d\n", rc); | ||
return rc; | ||
} | ||
|
||
|
@@ -72,6 +125,7 @@ static int secureSocketRecv(int fd, void *data, int len, char *userData) { | |
break; | ||
} | ||
} | ||
//printf("secureSocketRecv = %d\n", rc); | ||
return rc; | ||
} | ||
|
||
|
@@ -92,11 +146,12 @@ static int secureSocketSend(int fd, void *data, int len, char *userData) { | |
break; | ||
} | ||
} | ||
//printf("secureSocketSend = %d\n", rc); | ||
return rc; | ||
} | ||
|
||
int tlsSocketInit(TlsEnvironment *env, TlsSocket **outSocket, int fd, bool isServer) { | ||
int rc = 0; | ||
int rc = 0; | ||
gsk_iocallback ioCallbacks = {secureSocketRecv, secureSocketSend, NULL, NULL, NULL, NULL}; | ||
TlsSocket *socket = (TlsSocket*)safeMalloc(sizeof(TlsSocket), "Tls Socket"); | ||
if (!socket) { | ||
|
@@ -109,8 +164,7 @@ int tlsSocketInit(TlsEnvironment *env, TlsSocket **outSocket, int fd, bool isSer | |
if (label) { | ||
rc = rc || gsk_attribute_set_buffer(socket->socketHandle, GSK_KEYRING_LABEL, label, 0); | ||
} | ||
rc = rc || gsk_attribute_set_enum(socket->socketHandle, GSK_SESSION_TYPE, | ||
isServer ? GSK_SERVER_SESSION : GSK_CLIENT_SESSION); | ||
rc = rc || gsk_attribute_set_enum(socket->socketHandle, GSK_SESSION_TYPE, isServer ? GSK_SERVER_SESSION_WITH_CL_AUTH : GSK_CLIENT_SESSION); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will we never be able to use this without a client certificate on the server side? Asking because it's either I can see cases where one would want to use this code just for a secure connection but without client authentication required. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my understanding and testing, it just allows client authentication to be an option if a client certificate is attached. It behaves the same way as usual if you don't provide a client certificate. |
||
if (ciphers) { | ||
rc = rc || gsk_attribute_set_buffer(socket->socketHandle, GSK_V3_CIPHER_SPECS_EXPANDED, ciphers, 0); | ||
rc = rc || gsk_attribute_set_enum(socket->socketHandle, GSK_V3_CIPHERS, GSK_V3_CIPHERS_CHAR4); | ||
|
@@ -123,6 +177,7 @@ int tlsSocketInit(TlsEnvironment *env, TlsSocket **outSocket, int fd, bool isSer | |
safeFree((char*)socket, sizeof(*socket)); | ||
*outSocket = NULL; | ||
} | ||
//printf("tlsSocketInit - rc=%d\n", rc); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for this. |
||
return rc; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the password field to indicate whether this is a passwordless auth seems like a bad idea. If for some unexpected reason that field is
NULL
(is itNULL
if a user just doesn't provide a password in the basic auth?), you'll assume this is just a passwordless auth and will usezisCheckUsername
, which might say everything is okay.This seems like an error prone solution with implicit logic.
Regardless of whether this approach with client certificates is correct in the first place, using a separate variable to explicitly indicate what needs to be checked to authenticate would probably be better.