OM_uint32 ntlm_gss_inquire_context( OM_uint32 *minor_status, const gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, OM_uint32 *lifetime_rec, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *opened) { OM_uint32 ret = GSS_S_COMPLETE; ret = gss_inquire_context(minor_status, context_handle, src_name, targ_name, lifetime_rec, NULL, ctx_flags, locally_initiated, opened); if (mech_type) *mech_type = context_handle->mech_type; return (ret); }
static void log_service_name(gss_ctx_id_t context) { OM_uint32 major_status = 0, minor_status = 0; gss_name_t service_name; gss_buffer_desc service_name_buffer; major_status = gss_inquire_context(&minor_status, context, NULL, &service_name, NULL, NULL, NULL, NULL, NULL); if (major_status != GSS_S_COMPLETE) { log_status("gss_inquire_context", major_status, minor_status); return; } major_status = gss_display_name(&minor_status, service_name, &service_name_buffer, NULL); if (major_status == GSS_S_COMPLETE) { LOG_LOGINCONT(log_debug, "service principal is `%s'", service_name_buffer.value); gss_release_buffer(&minor_status, &service_name_buffer); } else log_status("gss_display_name", major_status, minor_status); gss_release_name(&minor_status, &service_name); }
uint32_t sapgss_inquire_context( uint32_t *minor_status, gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, uint32_t *lifetime_rec, sapgss_OID *mech_type, uint32_t *ctx_flags, int *locally_initiated, int *open) { gss_OID mech_type_loc; uint32_t major_status; int ret; major_status = gss_inquire_context(minor_status, context_handle, src_name, targ_name, lifetime_rec, &mech_type_loc, ctx_flags, locally_initiated, open); ret = gss_OID_loc_to_sap(mech_type_loc, mech_type); if (ret != 0) { *minor_status = ret; return GSS_S_FAILURE; } *mech_type = &gss_mech_krb5; return major_status; }
static inline bool authgss_ctx_expired(struct svc_rpc_gss_data *gd) { OM_uint32 maj_stat, min_stat; maj_stat = gss_inquire_context(&min_stat, gd->ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL); return (maj_stat == GSS_S_CONTEXT_EXPIRED); }
int gfarmGssInitiateSecurityContextResult( struct gfarmGssInitiateSecurityContextState *state, gss_ctx_id_t *scPtr, OM_uint32 *majStatPtr, OM_uint32 *minStatPtr, gss_name_t *remoteNamePtr) { int ret; OM_uint32 minStat2; assert(GSS_ERROR(state->majStat) || state->completed); if (state->itPtr->length > 0) { (void)gss_release_buffer(&minStat2, state->itPtr); } if (state->otPtr->length > 0) { (void)gss_release_buffer(&minStat2, state->otPtr); } if (state->majStat == GSS_S_COMPLETE && remoteNamePtr != NULL) { state->majStat = gss_inquire_context(&state->minStat, state->sc, NULL, remoteNamePtr, NULL, NULL, NULL, NULL, NULL); } gfarm_event_free(state->readable); gfarm_event_free(state->writable); if (majStatPtr != NULL) *majStatPtr = state->majStat; if (minStatPtr != NULL) *minStatPtr = state->minStat; if (state->majStat == GSS_S_COMPLETE) { *scPtr = state->sc; } else if (state->sc != GSS_C_NO_CONTEXT) { gss_delete_sec_context(&minStat2, &state->sc, GSS_C_NO_BUFFER); } ret = state->majStat == GSS_S_COMPLETE ? 1 : -1; free(state); if (ret == -1) { gflog_debug(GFARM_MSG_1000802, "failed to get result of initiate security context"); } return ret; }
char * Condor_Auth_X509::get_server_info() { OM_uint32 major_status = 0; OM_uint32 minor_status = 0; OM_uint32 lifetime, flags; gss_OID mech, name_type; gss_buffer_desc name_buf; char * server = NULL; // Now, we do some authorization work major_status = gss_inquire_context(&minor_status, context_handle, NULL, &m_gss_server_name, &lifetime, &mech, &flags, NULL, NULL); if (major_status != GSS_S_COMPLETE) { dprintf(D_SECURITY, "Unable to obtain target principal name\n"); return NULL; } major_status = gss_display_name(&minor_status, m_gss_server_name, &name_buf, &name_type); if( major_status != GSS_S_COMPLETE) { dprintf(D_SECURITY, "Unable to convert target principal name\n"); return NULL; } server = new char[name_buf.length+1]; memset(server, 0, name_buf.length+1); memcpy(server, name_buf.value, name_buf.length); gss_release_buffer( &minor_status, &name_buf ); return server; }
const char * getGSSAPISrcName( gss_ctx_id_t * inContext ) { char * theGSSAPISrcName = 0; OM_uint32 theMajorStatus; OM_uint32 theMinorStatus; gss_name_t theSrcName; theMajorStatus = gss_inquire_context( &theMinorStatus, *inContext, &theSrcName, 0, 0, 0, 0, 0, 0 ); if ( theMajorStatus == GSS_S_COMPLETE ) { gss_buffer_desc theSrcNameBuffer; theMajorStatus = gss_display_name( &theMinorStatus, theSrcName, &theSrcNameBuffer, 0 ); if ( theMajorStatus == GSS_S_COMPLETE ) { int theSize = theSrcNameBuffer.length + 1; theGSSAPISrcName = ( char * )malloc( theSize ); if ( theGSSAPISrcName != 0 ) { snprintf( theGSSAPISrcName, theSize, "%s", ( char *)theSrcNameBuffer.value ); } gss_release_buffer( &theMinorStatus, &theSrcNameBuffer ); } } return theGSSAPISrcName; }
OM_uint32 _gss_spnego_inquire_context ( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, gss_name_t * src_name, gss_name_t * targ_name, OM_uint32 * lifetime_rec, gss_OID * mech_type, OM_uint32 * ctx_flags, int * locally_initiated, int * open_context ) { gssspnego_ctx ctx; *minor_status = 0; if (context_handle == GSS_C_NO_CONTEXT) { return GSS_S_NO_CONTEXT; } ctx = (gssspnego_ctx)context_handle; if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { return GSS_S_NO_CONTEXT; } return gss_inquire_context(minor_status, ctx->negotiated_ctx_id, src_name, targ_name, lifetime_rec, mech_type, ctx_flags, locally_initiated, open_context); }
int verify_context(gss_ctx_id_t context_handle) { OM_uint32 major_status = 0; OM_uint32 minor_status = 0; OM_uint32 ret_flags = 0; gss_name_t target_name = GSS_C_NO_NAME; gss_name_t src_name = GSS_C_NO_NAME; gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER; char *target_name_str = NULL; char *src_name_str = NULL; int DN_cleaned = 0; int CNoffset, i; const char *ignored_CNs[] = {", CN=proxy", ", CN=limited proxy"}; major_status = gss_inquire_context( &minor_status, /* minor_status */ context_handle, /* context_handle */ &src_name, /* The client principal name */ &target_name, /* The server principal name */ NULL, /* don't need user_to_user */ NULL, /* don't need user_to_user */ NULL, /* don't need user_to_user */ NULL, /* don't need user_to_user */ NULL /* don't need user_to_user */ ); CHECK_GLOBUS_CALL("GSS context inquire failure ", 1, major_status); /* Get the server principal name */ major_status = gss_display_name( &minor_status, target_name, &name_buffer, NULL); CHECK_GLOBUS_CALL("GSS display_name failure ", 1, major_status); if ((target_name_str = (char *)malloc((name_buffer.length + 1) * sizeof(char))) == NULL) { fprintf(stderr, "verify_context(): Out of memory\n"); return(1); } memcpy(target_name_str, name_buffer.value, name_buffer.length); target_name_str[name_buffer.length] = '\0'; major_status = gss_release_name(&minor_status, &target_name); major_status = gss_release_buffer(&minor_status, &name_buffer); /* Get the client principal name */ major_status = gss_display_name( &minor_status, src_name, &name_buffer, NULL); CHECK_GLOBUS_CALL("GSS display_name failure ", 1, major_status); if ((src_name_str = (char *)malloc((name_buffer.length + 1) * sizeof(char))) == NULL) { fprintf(stderr, "verify_context(): Out of memory\n"); return(1); } memcpy(src_name_str, name_buffer.value, name_buffer.length); src_name_str[name_buffer.length] = '\0'; major_status = gss_release_name(&minor_status, &target_name); major_status = gss_release_buffer(&minor_status, &name_buffer); /* Strip trailing (limited) proxy CNs */ /* This is only needed (and effective) with very old */ /* versions of Globus. More recent versions return the */ /* pruned 'globusid' base name, making this stripping */ /* both useless and harmless. */ while (!DN_cleaned) { DN_cleaned = 1; for (i = 0; i < sizeof(ignored_CNs)/sizeof(char *); i++) { CNoffset = strlen(src_name_str) - strlen(ignored_CNs[i]); if (strcmp(src_name_str + CNoffset, ignored_CNs[i]) == 0) { src_name_str[CNoffset] = '\0'; DN_cleaned = 0; } } } DN_cleaned = 0; while (!DN_cleaned) { DN_cleaned = 1; for (i = 0; i < sizeof(ignored_CNs)/sizeof(char *); i++) { CNoffset = strlen(target_name_str) - strlen(ignored_CNs[i]); if (strcmp(target_name_str + CNoffset, ignored_CNs[i]) == 0) { target_name_str[CNoffset] = '\0'; DN_cleaned = 0; } } } return (strcmp(src_name_str, target_name_str)); }
gss_client_response *authenticate_gss_client_step(gss_client_state* state, const char* challenge) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_CONTINUE; gss_client_response *response = NULL; // Always clear out the old response if (state->response != NULL) { free(state->response); state->response = NULL; } // If there is a challenge (data from the server) we need to give it to GSS if (challenge && *challenge) { int len; input_token.value = base64_decode(challenge, &len); input_token.length = len; } // Do GSSAPI step maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &state->context, state->server_name, GSS_C_NO_OID, (OM_uint32)state->gss_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) { response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE; // Grab the client response to send back to the server if(output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length); maj_stat = gss_release_buffer(&min_stat, &output_token); } // Try to get the user name if we have completed all GSS operations if (ret == AUTH_GSS_COMPLETE) { gss_name_t gssuser = GSS_C_NO_NAME; maj_stat = gss_inquire_context(&min_stat, state->context, &gssuser, NULL, NULL, NULL, NULL, NULL, NULL); if(GSS_ERROR(maj_stat)) { response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } gss_buffer_desc name_token; name_token.length = 0; maj_stat = gss_display_name(&min_stat, gssuser, &name_token, NULL); if(GSS_ERROR(maj_stat)) { if(name_token.value) gss_release_buffer(&min_stat, &name_token); gss_release_name(&min_stat, &gssuser); response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } else { state->username = (char *)malloc(name_token.length + 1); strncpy(state->username, (char*) name_token.value, name_token.length); state->username[name_token.length] = 0; gss_release_buffer(&min_stat, &name_token); gss_release_name(&min_stat, &gssuser); } } end: if(output_token.value) gss_release_buffer(&min_stat, &output_token); if(input_token.value) free(input_token.value); if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); response->return_code = ret; } // Return the response return response; }
int gfarmGssInitiateSecurityContext(int fd, const gss_name_t acceptorName, gss_cred_id_t cred, OM_uint32 reqFlag, gss_ctx_id_t *scPtr, OM_uint32 *majStatPtr, OM_uint32 *minStatPtr, gss_name_t *remoteNamePtr) { OM_uint32 majStat; OM_uint32 minStat, minStat2; OM_uint32 retFlag = 0; gss_buffer_desc inputToken = GSS_C_EMPTY_BUFFER; gss_buffer_t itPtr = &inputToken; gss_buffer_desc outputToken = GSS_C_EMPTY_BUFFER; gss_buffer_t otPtr = &outputToken; gss_OID *actualMechType = NULL; OM_uint32 timeRet; int tknStat; static const char diag[] = "gfarmGssInitiateSecurityContext()"; *scPtr = GSS_C_NO_CONTEXT; /* * Implementation specification: * In gfarm, an initiator must reveal own identity to an acceptor. */ if ((reqFlag & GSS_C_ANON_FLAG) == GSS_C_ANON_FLAG) { /* It is a bit safer to deny the request than to silently ignore it */ gflog_auth_error(GFARM_MSG_1000618, "gfarmGssInitiateSecurityContext(): " "GSS_C_ANON_FLAG is not allowed"); majStat = GSS_S_UNAVAILABLE; minStat = GFSL_DEFAULT_MINOR_ERROR; goto Done; } while (1) { gfarm_mutex_lock(&gss_mutex, diag, gssDiag); majStat = gss_init_sec_context(&minStat, cred, scPtr, acceptorName, GSS_C_NO_OID, reqFlag, 0, GSS_C_NO_CHANNEL_BINDINGS, itPtr, actualMechType, otPtr, &retFlag, &timeRet); gfarm_mutex_unlock(&gss_mutex, diag, gssDiag); if (itPtr->length > 0) gss_release_buffer(&minStat2, itPtr); if (otPtr->length > 0) { tknStat = gfarmGssSendToken(fd, otPtr); gss_release_buffer(&minStat2, otPtr); if (tknStat <= 0) { gflog_auth_error(GFARM_MSG_1000619, "gfarmGssInitiateSecurityContext(): " "failed to send response"); majStat = GSS_S_DEFECTIVE_TOKEN|GSS_S_CALL_INACCESSIBLE_WRITE; minStat = GFSL_DEFAULT_MINOR_ERROR; } } if (GSS_ERROR(majStat)) { break; } if (majStat & GSS_S_CONTINUE_NEEDED) { tknStat = gfarmGssReceiveToken(fd, itPtr, GFARM_GSS_TIMEOUT_INFINITE); if (tknStat <= 0) { gflog_auth_error(GFARM_MSG_1000620, "gfarmGssInitiateSecurityContext(): " "failed to receive response"); majStat = GSS_S_DEFECTIVE_TOKEN|GSS_S_CALL_INACCESSIBLE_READ; minStat = GFSL_DEFAULT_MINOR_ERROR; break; } } else { break; } } if (itPtr->length > 0) gss_release_buffer(&minStat2, itPtr); if (majStat == GSS_S_COMPLETE && remoteNamePtr != NULL) { majStat = gss_inquire_context(&minStat, *scPtr, NULL, remoteNamePtr, NULL, NULL, NULL, NULL, NULL); } Done: if (majStatPtr != NULL) *majStatPtr = majStat; if (minStatPtr != NULL) *minStatPtr = minStat; if (majStat != GSS_S_COMPLETE && *scPtr != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minStat2, scPtr, GSS_C_NO_BUFFER); return majStat == GSS_S_COMPLETE ? 1 : -1; }
/* * AEAD wrap API for a single piece of associated data, for compatibility * with MIT and as specified by draft-howard-gssapi-aead-00.txt. * * @ingroup gssapi */ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_wrap_aead(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_assoc_buffer, gss_buffer_t input_payload_buffer, int *conf_state, gss_buffer_t output_message_buffer) { OM_uint32 major_status, tmp, flags = 0; gss_iov_buffer_desc iov[5]; size_t i; unsigned char *p; memset(iov, 0, sizeof(iov)); iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; if (input_assoc_buffer) iov[1].buffer = *input_assoc_buffer; iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; if (input_payload_buffer) iov[2].buffer.length = input_payload_buffer->length; gss_inquire_context(minor_status, context_handle, NULL, NULL, NULL, NULL, &flags, NULL, NULL); /* krb5 mech rejects padding/trailer if DCE-style is set */ iov[3].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY : GSS_IOV_BUFFER_TYPE_PADDING; iov[4].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY : GSS_IOV_BUFFER_TYPE_TRAILER; major_status = gss_wrap_iov_length(minor_status, context_handle, conf_req_flag, qop_req, conf_state, iov, 5); if (GSS_ERROR(major_status)) return major_status; for (i = 0, output_message_buffer->length = 0; i < 5; i++) { if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) continue; output_message_buffer->length += iov[i].buffer.length; } output_message_buffer->value = malloc(output_message_buffer->length); if (output_message_buffer->value == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } for (i = 0, p = output_message_buffer->value; i < 5; i++) { if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) continue; else if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA) memcpy(p, input_payload_buffer->value, input_payload_buffer->length); iov[i].buffer.value = p; p += iov[i].buffer.length; } major_status = gss_wrap_iov(minor_status, context_handle, conf_req_flag, qop_req, conf_state, iov, 5); if (GSS_ERROR(major_status)) gss_release_buffer(&tmp, output_message_buffer); return major_status; }
DWORD LWIGetGSSSecurityContextInfo( gss_ctx_id_t gss_ctx, PSTR *pszClientName ) { DWORD dwError = ERROR_SUCCESS; int gss_rc = 0; OM_uint32 minor_status = 0; gss_name_t src = GSS_C_NO_NAME; gss_buffer_desc src_name = GSS_C_EMPTY_BUFFER; gss_OID src_type = GSS_C_NULL_OID; PSTR pszClient = NULL; /* Fetch security context information to make it available on the server side (e.g. for security checks) */ gss_rc = gss_inquire_context(&minor_status, gss_ctx, &src, NULL, NULL, NULL, NULL, NULL, NULL); if (gss_rc == GSS_S_COMPLETE) { /* * Get calling principal name */ gss_rc = gss_display_name(&minor_status, src, &src_name, &src_type); if (gss_rc != GSS_S_COMPLETE) { /* * TODO: error handling */ } dwError = EVTStrndup(src_name.value, src_name.length, &pszClient); BAIL_ON_EVT_ERROR(dwError); } else { /* error handling */ } *pszClientName = pszClient; cleanup: gss_release_buffer(&minor_status, &src_name); if (src) { gss_release_name(&minor_status, &src); } return dwError; error: EVT_SAFE_FREE_STRING(pszClient); *pszClientName = NULL; goto cleanup; }
int gsscon_authorize (gss_ctx_id_t inContext, int *outAuthorized, int *outAuthorizationError) { int err = 0; OM_uint32 majorStatus; OM_uint32 minorStatus = 0; gss_name_t clientName = NULL; gss_name_t serviceName = NULL; char *clientPrincipal = NULL; char *servicePrincipal = NULL; if (!inContext ) { err = EINVAL; } if (!outAuthorized ) { err = EINVAL; } if (!outAuthorizationError) { err = EINVAL; } if (!err) { /* Get the client and service principals used to authenticate */ majorStatus = gss_inquire_context (&minorStatus, inContext, &clientName, &serviceName, NULL, NULL, NULL, NULL, NULL); if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } } if (!err) { /* Pull the client principal string out of the gss name */ gss_buffer_desc nameToken; majorStatus = gss_display_name (&minorStatus, clientName, &nameToken, NULL); if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } if (!err) { clientPrincipal = malloc (nameToken.length + 1); if (clientPrincipal == NULL) { err = ENOMEM; } } if (!err) { memcpy (clientPrincipal, nameToken.value, nameToken.length); clientPrincipal[nameToken.length] = '\0'; } if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); } } if (!err) { // /* Pull the service principal string out of the gss name */ // gss_buffer_desc nameToken; // // majorStatus = gss_display_name (&minorStatus, // serviceName, // &nameToken, // NULL); // if (majorStatus != GSS_S_COMPLETE) { // err = minorStatus ? minorStatus : majorStatus; // } // // if (!err) { // servic7ePrincipal = malloc (nameToken.length + 1); // if (servicePrincipal == NULL) { err = ENOMEM; } // } // // if (!err) { // memcpy (servicePrincipal, nameToken.value, nameToken.length); // servicePrincipal[nameToken.length] = '\0'; // } // if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); } // } int authorizationErr = 0; authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal); // printf ("'%s' is%s authorized for service '%s'\n", // clientPrincipal, authorizationErr ? " NOT" : "", servicePrincipal); // *outAuthorized = !authorizationErr; *outAuthorizationError = authorizationErr; } if (serviceName ) { gss_release_name (&minorStatus, &serviceName); } if (clientName ) { gss_release_name (&minorStatus, &clientName); } if (clientPrincipal ) { free (clientPrincipal); } if (servicePrincipal) { free (servicePrincipal); } return err; }
/** * Initialize GSI Authentication. * This method asks the server for authentication. * @param sock the socket descriptot * @return true on success, false otherwise. */ bool GSISocketClient::InitGSIAuthentication(int sock) { OM_uint32 major_status = 0; OM_uint32 minor_status = 0; gss_cred_id_t credential = GSS_C_NO_CREDENTIAL; OM_uint32 req_flags = 0; OM_uint32 ret_flags = 0; int token_status = 0; bool return_status = false; gss_name_t targ_name; gss_buffer_desc name_buffer; char service[1024]; //acquire our credentials major_status = globus_gss_assist_acquire_cred(&minor_status, GSS_C_BOTH, &credential); if(major_status != GSS_S_COMPLETE) { char buf[32]; std::string msg(FAILED_ACQ_CRED); sprintf(buf, "%d", port); msg.append(host + ":" + std::string(buf)); char *gssmsg = NULL; globus_gss_assist_display_status_str( &gssmsg, NULL, major_status, minor_status, token_status); std::string source(gssmsg); free(gssmsg); return false; } //Request that remote peer authenticate tself req_flags = GSS_C_MUTUAL_FLAG; if(_delegate_credentials) { req_flags |= GSS_C_DELEG_FLAG; } snprintf(service, sizeof(service), "host@%s", host.c_str()); //initialize the security context // credential has to be fill in beforehand std::pair<int,int> arg(sock, m_auth_timeout); major_status = globus_gss_assist_init_sec_context(&minor_status, credential, &gss_context, _server_contact.empty() ? service :(char*) _server_contact.c_str(), req_flags, &ret_flags, &token_status, get_token, (void *) &arg, send_token, (void *) &arg); gss_release_cred(&minor_status, &credential); if(major_status != GSS_S_COMPLETE) { char *gssmsg = NULL; globus_gss_assist_display_status_str(&gssmsg, NULL, major_status, minor_status, token_status); if(gss_context != GSS_C_NO_CONTEXT) { gss_delete_sec_context(&minor_status, &gss_context, GSS_C_NO_BUFFER); } std::string source(gssmsg); free(gssmsg); return_status = false; } else { major_status = gss_inquire_context(&minor_status, gss_context, NULL, &targ_name, NULL, NULL, NULL, NULL, NULL); return_status = (major_status == GSS_S_COMPLETE); major_status = gss_display_name(&minor_status, targ_name, &name_buffer, NULL); gss_release_name(&minor_status, &targ_name); } if (return_status == false && gss_context != GSS_C_NO_CONTEXT) { gss_delete_sec_context(&minor_status, &gss_context, GSS_C_NO_BUFFER); } if (return_status == false) { char *gssmsg = NULL; globus_gss_assist_display_status_str( &gssmsg, NULL, major_status, minor_status, token_status); std::string source(gssmsg); free(gssmsg); } return return_status; }
/** * @brief Compares two RPC creds * * @param[in] cred1 First RPC cred * @param[in] cred2 Second RPC cred * * @return true if same, false otherwise */ bool nfs_compare_clientcred(nfs_client_cred_t *cred1, nfs_client_cred_t *cred2) { #ifdef _HAVE_GSSAPI gss_name_t cred1_cred_name; gss_name_t cred2_cred_name; OM_uint32 maj_stat, min_stat; int status; #endif if (cred1 == NULL) return false; if (cred2 == NULL) return false; if (cred1->flavor != cred2->flavor) return false; switch (cred1->flavor) { case AUTH_UNIX: if (cred1->auth_union.auth_unix.aup_uid != cred2->auth_union.auth_unix.aup_uid) return false; if (cred1->auth_union.auth_unix.aup_gid != cred2->auth_union.auth_unix.aup_gid) return false; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: maj_stat = gss_inquire_context(&min_stat, cred1->auth_union.auth_gss.gss_context_id, &cred1_cred_name, NULL, NULL, NULL, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTEXT_EXPIRED) return false; maj_stat = gss_inquire_context(&min_stat, cred2->auth_union.auth_gss.gss_context_id, &cred2_cred_name, NULL, NULL, NULL, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTEXT_EXPIRED) return false; maj_stat = gss_compare_name(&min_stat, cred1_cred_name, cred2_cred_name, &status); if (maj_stat != GSS_S_COMPLETE) return false; if (status == 0) return false; break; #endif default: if (memcmp (&cred1->auth_union, &cred2->auth_union, cred1->length)) return false; break; } /* If this point is reached, structures are the same */ return true; }
int GSI_SOCKET_authentication_init(GSI_SOCKET *self, char *accepted_peer_names[]) { int token_status; gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; gss_name_t server_gss_name = GSS_C_NO_NAME; OM_uint32 req_flags = 0, ret_flags = 0; int return_value = GSI_SOCKET_ERROR; gss_buffer_desc gss_buffer = { 0 }, tmp_gss_buffer = { 0 }; gss_name_t target_name = GSS_C_NO_NAME; gss_OID target_name_type = GSS_C_NO_OID; int i, rc=0, sock; FILE *fp = NULL; char *cert_dir = NULL; globus_result_t res; if (self == NULL) { return GSI_SOCKET_ERROR; } if (accepted_peer_names == NULL || accepted_peer_names[0] == NULL) { return GSI_SOCKET_ERROR; } if (self->gss_context != GSS_C_NO_CONTEXT) { GSI_SOCKET_set_error_string(self, "GSI_SOCKET already authenticated"); goto error; } res = GLOBUS_GSI_SYSCONFIG_GET_CERT_DIR(&cert_dir); if (res == GLOBUS_SUCCESS) { myproxy_debug("using trusted certificates directory %s", cert_dir); } else { verror_put_string("error getting trusted certificates directory"); globus_error_to_verror(res); goto error; } self->major_status = globus_gss_assist_acquire_cred(&self->minor_status, GSS_C_INITIATE, &creds); if (self->major_status != GSS_S_COMPLETE) { if (self->allow_anonymous) { req_flags |= GSS_C_ANON_FLAG; myproxy_debug("no valid credentials found -- " "performing anonymous authentication"); } else { goto error; } } req_flags |= GSS_C_REPLAY_FLAG; req_flags |= GSS_C_MUTUAL_FLAG; req_flags |= GSS_C_CONF_FLAG; req_flags |= GSS_C_INTEG_FLAG; if ((sock = dup(self->sock)) < 0) { GSI_SOCKET_set_error_string(self, "dup() of socket fd failed"); self->error_number = errno; goto error; } if ((fp = fdopen(sock, "r")) == NULL) { GSI_SOCKET_set_error_string(self, "fdopen() of socket failed"); self->error_number = errno; goto error; } if (setvbuf(fp, NULL, _IONBF, 0) != 0) { GSI_SOCKET_set_error_string(self, "setvbuf() for socket failed"); self->error_number = errno; goto error; } self->major_status = globus_gss_assist_init_sec_context(&self->minor_status, creds, &self->gss_context, "GSI-NO-TARGET", req_flags, &ret_flags, &token_status, globus_gss_assist_token_get_fd, (void *)fp, assist_write_token, (void *)&self->sock); if (self->major_status != GSS_S_COMPLETE) { goto error; } /* Verify that all service requests were honored. */ req_flags &= ~(GSS_C_ANON_FLAG); /* GSI GSSAPI doesn't set this flag */ if ((req_flags & ret_flags) != req_flags) { GSI_SOCKET_set_error_string(self, "requested GSSAPI service not supported"); goto error; } if (ret_flags & GSS_C_GLOBUS_LIMITED_PROXY_FLAG) { self->limited_proxy = 1; } /* Check the authenticated identity of the server. */ self->major_status = gss_inquire_context(&self->minor_status, self->gss_context, NULL, &server_gss_name, NULL, NULL, NULL, NULL, NULL); if (self->major_status != GSS_S_COMPLETE) { GSI_SOCKET_set_error_string(self, "gss_inquire_context() failed"); goto error; } self->major_status = gss_display_name(&self->minor_status, server_gss_name, &gss_buffer, NULL); if (self->major_status != GSS_S_COMPLETE) { GSI_SOCKET_set_error_string(self, "gss_display_name() failed"); goto error; } self->peer_name = strdup(gss_buffer.value); myproxy_debug("server name: %s", self->peer_name); myproxy_debug("checking that server name is acceptable..."); /* We told gss_assist_init_sec_context() not to check the server name so we can check it manually here. */ for (i=0; accepted_peer_names[i] != NULL; i++) { tmp_gss_buffer.value = (void *)accepted_peer_names[i]; tmp_gss_buffer.length = strlen(accepted_peer_names[i]); if (strchr(accepted_peer_names[i],'@') && !strstr(accepted_peer_names[i],"CN=")) { target_name_type = GSS_C_NT_HOSTBASED_SERVICE; } else { target_name_type = GSS_C_NO_OID; } self->major_status = gss_import_name(&self->minor_status, &tmp_gss_buffer, target_name_type, &target_name); if (self->major_status != GSS_S_COMPLETE) { char error_string[550]; sprintf(error_string, "failed to import GSS name \"%.500s\"", accepted_peer_names[i]); GSI_SOCKET_set_error_string(self, error_string); goto error; } self->major_status = gss_compare_name(&self->minor_status, server_gss_name, target_name, &rc); gss_release_name(&self->minor_status, &target_name); if (self->major_status != GSS_S_COMPLETE) { char error_string[1050]; sprintf(error_string, "gss_compare_name(\"%.500s\",\"%.500s\") failed", self->peer_name, accepted_peer_names[i]); GSI_SOCKET_set_error_string(self, error_string); goto error; } if (rc) { myproxy_debug("server name matches \"%s\"", accepted_peer_names[i]); break; } else { myproxy_debug("server name does not match \"%s\"", accepted_peer_names[i]); } } if (!rc) { /* no match with acceptable target names */ GSI_SOCKET_set_error_string(self, "authenticated peer name does not match"); return_value = GSI_SOCKET_UNAUTHORIZED; goto error; } myproxy_debug("authenticated server name is acceptable"); /* Success */ return_value = GSI_SOCKET_SUCCESS; error: { OM_uint32 minor_status; gss_release_cred(&minor_status, &creds); gss_release_buffer(&minor_status, &gss_buffer); gss_release_name(&minor_status, &server_gss_name); } if (cert_dir) free(cert_dir); if (fp) fclose(fp); return return_value; }
static int check_iprop_rpcsec_auth(struct svc_req *rqstp) { /* XXX Since the client can authenticate against any principal in the database, we need to do a sanity check. Only checking for "kiprop" now, but that means theoretically the client could be authenticating to kiprop on some other machine. */ /* Code taken from kadm_rpc_svc.c, tweaked. */ gss_ctx_id_t ctx; krb5_context kctx; OM_uint32 maj_stat, min_stat; gss_name_t name; krb5_principal princ; int ret, success; krb5_data *c1, *c2, *realm; gss_buffer_desc gss_str; kadm5_server_handle_t handle; size_t slen; char *sdots; success = 0; handle = (kadm5_server_handle_t)global_server_handle; if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) return 0; ctx = rqstp->rq_svccred; maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name, NULL, NULL, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE) { krb5_klog_syslog(LOG_ERR, "check_rpcsec_auth: " "failed inquire_context, stat=%u", maj_stat); log_badauth(maj_stat, min_stat, &rqstp->rq_xprt->xp_raddr, NULL); goto fail_name; } kctx = handle->context; ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str); if (ret == 0) goto fail_name; slen = gss_str.length; trunc_name(&slen, &sdots); /* * Since we accept with GSS_C_NO_NAME, the client can authenticate * against the entire kdb. Therefore, ensure that the service * name is something reasonable. */ if (krb5_princ_size(kctx, princ) != 2) goto fail_princ; c1 = krb5_princ_component(kctx, princ, 0); c2 = krb5_princ_component(kctx, princ, 1); realm = krb5_princ_realm(kctx, princ); if (strncmp(handle->params.realm, realm->data, realm->length) == 0 && strncmp("kiprop", c1->data, c1->length) == 0) { success = 1; } fail_princ: if (!success) { krb5_klog_syslog(LOG_ERR, "bad service principal %.*s%s", (int) slen, (char *) gss_str.value, sdots); } gss_release_buffer(&min_stat, &gss_str); krb5_free_principal(kctx, princ); fail_name: gss_release_name(&min_stat, &name); return success; }
static int gss_auth(void *app_data, char *host) { OM_uint32 maj_stat, min_stat; gss_name_t target_name; gss_buffer_desc input, output_token; int context_established = 0; char *p; int n; gss_channel_bindings_t bindings; struct gssapi_data *d = app_data; OM_uint32 mech_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; const char *knames[] = { "ftp", "host", NULL }, **kname = knames; if(import_name(*kname++, host, &target_name)) return AUTH_ERROR; input.length = 0; input.value = NULL; if (ftp_do_gss_bindings) { bindings = malloc(sizeof(*bindings)); if (bindings == NULL) errx(1, "out of memory"); sockaddr_to_gss_address (myctladdr, &bindings->initiator_addrtype, &bindings->initiator_address); sockaddr_to_gss_address (hisctladdr, &bindings->acceptor_addrtype, &bindings->acceptor_address); bindings->application_data.length = 0; bindings->application_data.value = NULL; } else bindings = GSS_C_NO_CHANNEL_BINDINGS; if (ftp_do_gss_delegate) mech_flags |= GSS_C_DELEG_FLAG; while(!context_established) { maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &d->context_hdl, target_name, GSS_C_NO_OID, mech_flags, 0, bindings, &input, NULL, &output_token, NULL, NULL); if (GSS_ERROR(maj_stat)) { OM_uint32 new_stat; OM_uint32 msg_ctx = 0; gss_buffer_desc status_string; d->context_hdl = GSS_C_NO_CONTEXT; gss_release_name(&min_stat, &target_name); if(*kname != NULL) { if(import_name(*kname++, host, &target_name)) { if (bindings != GSS_C_NO_CHANNEL_BINDINGS) free(bindings); return AUTH_ERROR; } continue; } if (bindings != GSS_C_NO_CHANNEL_BINDINGS) free(bindings); gss_display_status(&new_stat, min_stat, GSS_C_MECH_CODE, GSS_C_NO_OID, &msg_ctx, &status_string); printf("Error initializing security context: %.*s\n", (int)status_string.length, (char*)status_string.value); gss_release_buffer(&new_stat, &status_string); return AUTH_CONTINUE; } if (input.value) { free(input.value); input.value = NULL; input.length = 0; } if (output_token.length != 0) { base64_encode(output_token.value, output_token.length, &p); gss_release_buffer(&min_stat, &output_token); n = command("ADAT %s", p); free(p); } if (GSS_ERROR(maj_stat)) { if (d->context_hdl != GSS_C_NO_CONTEXT) gss_delete_sec_context (&min_stat, &d->context_hdl, GSS_C_NO_BUFFER); break; } if (maj_stat & GSS_S_CONTINUE_NEEDED) { p = strstr(reply_string, "ADAT="); if(p == NULL){ printf("Error: expected ADAT in reply. got: %s\n", reply_string); if (bindings != GSS_C_NO_CHANNEL_BINDINGS) free(bindings); return AUTH_ERROR; } else { p+=5; input.value = malloc(strlen(p)); input.length = base64_decode(p, input.value); } } else { if(code != 235) { printf("Unrecognized response code: %d\n", code); if (bindings != GSS_C_NO_CHANNEL_BINDINGS) free(bindings); return AUTH_ERROR; } context_established = 1; } } gss_release_name(&min_stat, &target_name); if (bindings != GSS_C_NO_CHANNEL_BINDINGS) free(bindings); if (input.value) free(input.value); { gss_name_t targ_name; maj_stat = gss_inquire_context(&min_stat, d->context_hdl, NULL, &targ_name, NULL, NULL, NULL, NULL, NULL); if (GSS_ERROR(maj_stat) == 0) { gss_buffer_desc name; maj_stat = gss_display_name (&min_stat, targ_name, &name, NULL); if (GSS_ERROR(maj_stat) == 0) { printf("Authenticated to <%.*s>\n", (int)name.length, (char *)name.value); gss_release_buffer(&min_stat, &name); } gss_release_name(&min_stat, &targ_name); } else printf("Failed to get gss name of peer.\n"); } return AUTH_OK; }
OM_uint32 gssi_inquire_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, OM_uint32 *lifetime_rec, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *open) { struct gpp_context_handle *ctx_handle; struct gpp_name_handle *s_name = NULL; struct gpp_name_handle *t_name = NULL; gss_OID mech_oid; OM_uint32 maj, min; GSSI_TRACE(); if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle->local && !ctx_handle->remote) { return GSS_S_CALL_INACCESSIBLE_READ; } if (src_name) { s_name = calloc(1, sizeof(struct gpp_name_handle)); if (!s_name) { min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } if (targ_name) { t_name = calloc(1, sizeof(struct gpp_name_handle)); if (!t_name) { min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } if (ctx_handle->local) { maj = gss_inquire_context(&min, ctx_handle->local, s_name ? &s_name->local : NULL, t_name ? &t_name->local : NULL, lifetime_rec, &mech_oid, ctx_flags, locally_initiated, open); } else { maj = gpm_inquire_context(&min, ctx_handle->remote, s_name ? &s_name->remote : NULL, t_name ? &t_name->remote : NULL, lifetime_rec, &mech_oid, ctx_flags, locally_initiated, open); } if (maj != GSS_S_COMPLETE) { goto done; } if (s_name) { maj = gpp_copy_oid(&min, mech_oid, &s_name->mech_type); if (maj != GSS_S_COMPLETE) { goto done; } } if (t_name) { maj = gpp_copy_oid(&min, mech_oid, &t_name->mech_type); if (maj != GSS_S_COMPLETE) { goto done; } } done: *minor_status = gpp_map_error(min); if (maj == GSS_S_COMPLETE) { if (mech_type) { *mech_type = mech_oid; } else { (void)gss_release_oid(&min, &mech_oid); } if (src_name) { *src_name = (gss_name_t)s_name; } if (targ_name) { *targ_name = (gss_name_t)t_name; } } else { (void)gss_release_oid(&min, &mech_oid); (void)gssi_release_name(&min, (gss_name_t *)&s_name); (void)gssi_release_name(&min, (gss_name_t *)&t_name); } return maj; }
gss_client_response *authenticate_gss_server_step(gss_server_state *state, const char *auth_data) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_CONTINUE; gss_client_response *response = NULL; // Always clear out the old response if (state->response != NULL) { free(state->response); state->response = NULL; } // we don't need to check the authentication token if S4U2Self protocol // transition was done, because we already have the client credentials. if (state->client_creds == GSS_C_NO_CREDENTIAL) { if (auth_data && *auth_data) { int len; input_token.value = base64_decode(auth_data, &len); input_token.length = len; } else { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->message = strdup("No auth_data value in request from client"); response->return_code = AUTH_GSS_ERROR; goto end; } maj_stat = gss_accept_sec_context(&min_stat, &state->context, state->server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &state->client_name, NULL, &output_token, NULL, NULL, &state->client_creds); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_accept_sec_context", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } // Grab the server response to send back to the client if (output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length); maj_stat = gss_release_buffer(&min_stat, &output_token); } } // Get the user name maj_stat = gss_display_name(&min_stat, state->client_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_display_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } state->username = (char *)malloc(output_token.length + 1); strncpy(state->username, (char*) output_token.value, output_token.length); state->username[output_token.length] = 0; // Get the target name if no server creds were supplied if (state->server_creds == GSS_C_NO_CREDENTIAL) { gss_name_t target_name = GSS_C_NO_NAME; maj_stat = gss_inquire_context(&min_stat, state->context, NULL, &target_name, NULL, NULL, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_inquire_context", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_display_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } state->targetname = (char *)malloc(output_token.length + 1); strncpy(state->targetname, (char*) output_token.value, output_token.length); state->targetname[output_token.length] = 0; } if (state->constrained_delegation && state->client_creds != GSS_C_NO_CREDENTIAL) { if ((response = store_gss_creds(state)) != NULL) { goto end; } } ret = AUTH_GSS_COMPLETE; end: if (output_token.length) gss_release_buffer(&min_stat, &output_token); if (input_token.value) free(input_token.value); if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->return_code = ret; } // Return the response return response; }
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct connectdata *conn) { struct SessionHandle *data = conn->data; curl_socket_t sock = conn->sock[sockindex]; CURLcode code; ssize_t actualread; ssize_t written; int result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; int gss_conf_state, gss_enc; gss_buffer_desc service = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc* gss_token = GSS_C_NO_BUFFER; gss_name_t server = GSS_C_NO_NAME; gss_name_t gss_client_name = GSS_C_NO_NAME; unsigned short us_length; char *user=NULL; unsigned char socksreq[4]; /* room for gssapi exchange header only */ char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE]; /* GSSAPI request looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ /* prepare service name */ if(strchr(serviceptr,'/')) { service.value = malloc(strlen(serviceptr)); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr); memcpy(service.value, serviceptr, service.length); gss_major_status = gss_import_name(&gss_minor_status, &service, (gss_OID) GSS_C_NULL_OID, &server); } else { service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1; snprintf(service.value, service.length+1, "%s@%s", serviceptr, conn->proxy.name); gss_major_status = gss_import_name(&gss_minor_status, &service, gss_nt_service_name, &server); } gss_release_buffer(&gss_status, &service); /* clear allocated memory */ if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_import_name()")) { failf(data, "Failed to create service name."); gss_release_name(&gss_status, &server); return CURLE_COULDNT_CONNECT; } /* As long as we need to keep sending some context info, and there's no */ /* errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, &gss_context, server, NULL, gss_token, &gss_send_token, &gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) gss_release_buffer(&gss_status, &gss_recv_token); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_init_sec_context")) { gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to initial GSSAPI token."); return CURLE_COULDNT_CONNECT; } if(gss_send_token.length != 0) { socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 1; /* authentication message type */ us_length = htons((short)gss_send_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } code = Curl_write_plain(conn, sock, (char *)gss_send_token.value, gss_send_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) { failf(data, "Failed to send GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_recv_token); if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; /* analyse response */ /* GSSAPI response looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI authentication response."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 1) { /* status / messgae type */ failf(data, "Invalid GSSAPI authentication response type (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length=us_length; gss_recv_token.value=malloc(us_length); if(!gss_recv_token.value) { failf(data, "Could not allocate memory for GSSAPI authentication " "response token."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_token = &gss_recv_token; } gss_release_name(&gss_status, &server); /* Everything is good so far, user was authenticated! */ gss_major_status = gss_inquire_context (&gss_minor_status, gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_inquire_context")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, &gss_send_token, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_display_name")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } user=malloc(gss_send_token.length+1); if(!user) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); return CURLE_OUT_OF_MEMORY; } memcpy(user, gss_send_token.value, gss_send_token.length); user[gss_send_token.length] = '\0'; gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user); free(user); user=NULL; /* Do encryption */ socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 2; /* encryption message type */ gss_enc = 0; /* no data protection */ /* do confidentiality protection if supported */ if(gss_ret_flags & GSS_C_CONF_FLAG) gss_enc = 2; /* else do integrity protection */ else if(gss_ret_flags & GSS_C_INTEG_FLAG) gss_enc = 1; infof(data, "SOCKS5 server supports gssapi %s data protection.\n", (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality")); /* force for the moment to no data protection */ gss_enc = 0; /* * Sending the encryption type in clear seems wrong. It should be * protected with gss_seal()/gss_wrap(). See RFC1961 extract below * The NEC reference implementations on which this is based is * therefore at fault * * +------+------+------+.......................+ * + ver | mtyp | len | token | * +------+------+------+.......................+ * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | * +------+------+------+.......................+ * * Where: * * - "ver" is the protocol version number, here 1 to represent the * first version of the SOCKS/GSS-API protocol * * - "mtyp" is the message type, here 2 to represent a protection * -level negotiation message * * - "len" is the length of the "token" field in octets * * - "token" is the GSS-API encapsulated protection level * * The token is produced by encapsulating an octet containing the * required protection level using gss_seal()/gss_wrap() with conf_req * set to FALSE. The token is verified using gss_unseal()/ * gss_unwrap(). * */ if(data->set.socks5_gssapi_nec) { us_length = htons((short)1); memcpy(socksreq+2,&us_length,sizeof(short)); } else { gss_send_token.length = 1; gss_send_token.value = malloc(1); if(!gss_send_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } memcpy(gss_send_token.value, &gss_enc, 1); gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, GSS_C_QOP_DEFAULT, &gss_send_token, &gss_conf_state, &gss_w_token); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) { gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_send_token); us_length = htons((short)gss_w_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); } code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); code = Curl_write_plain(conn, sock, socksreq, 1, &written); if((code != CURLE_OK) || ( 1 != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { code = Curl_write_plain(conn, sock, (char *)gss_w_token.value, gss_w_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); } result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / messgae type */ failf(data, "Invalid GSSAPI encryption response type (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length= us_length; gss_recv_token.value=malloc(gss_recv_token.length); if(!gss_recv_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI encryptrion type."); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { gss_major_status = gss_unwrap(&gss_minor_status, gss_context, &gss_recv_token, &gss_w_token, 0, GSS_C_QOP_DEFAULT); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) { gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_recv_token); if(gss_w_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_w_token.value,gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); } else { if(gss_recv_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_recv_token.value,gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); } infof(data, "SOCKS5 access with%s protection granted.\n", (socksreq[0]==0)?"out gssapi data": ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality")); conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OK; }
int main(int argc, char **argv) { struct http_req req; const char *host, *page; int i, done, print_body, gssapi_done, gssapi_started; char *headers[10]; /* XXX */ int num_headers; gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT; gss_name_t server = GSS_C_NO_NAME; int optind = 0; gss_OID mech_oid; OM_uint32 flags; setprogname(argv[0]); if(getarg(http_args, num_http_args, argc, argv, &optind)) usage(1); if (help_flag) usage (0); if(version_flag) { print_version(NULL); exit(0); } argc -= optind; argv += optind; mech_oid = select_mech(mech); if (argc != 1 && argc != 2) errx(1, "usage: %s host [page]", getprogname()); host = argv[0]; if (argc == 2) page = argv[1]; else page = "/"; flags = 0; if (delegate_flag) flags |= GSS_C_DELEG_FLAG; if (mutual_flag) flags |= GSS_C_MUTUAL_FLAG; done = 0; num_headers = 0; gssapi_done = 1; gssapi_started = 0; do { print_body = 0; http_query(host, page, headers, num_headers, &req); for (i = 0 ; i < num_headers; i++) free(headers[i]); num_headers = 0; if (strstr(req.response, " 200 ") != NULL) { print_body = 1; done = 1; } else if (strstr(req.response, " 401 ") != NULL) { if (http_find_header(&req, "WWW-Authenticate:") == NULL) errx(1, "Got %s but missed `WWW-Authenticate'", req.response); gssapi_done = 0; } if (!gssapi_done) { const char *h = http_find_header(&req, "WWW-Authenticate:"); if (h == NULL) errx(1, "Got %s but missed `WWW-Authenticate'", req.response); if (strncasecmp(h, "Negotiate", 9) == 0) { OM_uint32 maj_stat, min_stat; gss_buffer_desc input_token, output_token; if (verbose_flag) printf("Negotiate found\n"); if (server == GSS_C_NO_NAME) { char *name; asprintf(&name, "%s@%s", gss_service, host); input_token.length = strlen(name); input_token.value = name; maj_stat = gss_import_name(&min_stat, &input_token, GSS_C_NT_HOSTBASED_SERVICE, &server); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_inport_name"); free(name); input_token.length = 0; input_token.value = NULL; } i = 9; while(h[i] && isspace((unsigned char)h[i])) i++; if (h[i] != '\0') { int len = strlen(&h[i]); if (len == 0) errx(1, "invalid Negotiate token"); input_token.value = emalloc(len); len = base64_decode(&h[i], input_token.value); if (len < 0) errx(1, "invalid base64 Negotiate token %s", &h[i]); input_token.length = len; } else { if (gssapi_started) errx(1, "Negotiate already started"); gssapi_started = 1; input_token.length = 0; input_token.value = NULL; } maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context_hdl, server, mech_oid, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_init_sec_context"); else if (maj_stat & GSS_S_CONTINUE_NEEDED) gssapi_done = 0; else { gss_name_t targ_name, src_name; gss_buffer_desc name_buffer; gss_OID mech_type; gssapi_done = 1; printf("Negotiate done: %s\n", mech); maj_stat = gss_inquire_context(&min_stat, context_hdl, &src_name, &targ_name, NULL, &mech_type, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_inquire_context"); maj_stat = gss_display_name(&min_stat, src_name, &name_buffer, NULL); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_display_name"); printf("Source: %.*s\n", (int)name_buffer.length, (char *)name_buffer.value); gss_release_buffer(&min_stat, &name_buffer); maj_stat = gss_display_name(&min_stat, targ_name, &name_buffer, NULL); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_display_name"); printf("Target: %.*s\n", (int)name_buffer.length, (char *)name_buffer.value); gss_release_name(&min_stat, &targ_name); gss_release_buffer(&min_stat, &name_buffer); } if (output_token.length) { char *neg_token; base64_encode(output_token.value, output_token.length, &neg_token); asprintf(&headers[0], "Authorization: Negotiate %s", neg_token); num_headers = 1; free(neg_token); gss_release_buffer(&min_stat, &output_token); } if (input_token.length) free(input_token.value); } else done = 1; } else done = 1; if (verbose_flag) { printf("%s\n\n", req.response); for (i = 0; i < req.num_headers; i++) printf("%s\n", req.headers[i]); printf("\n"); } if (print_body || verbose_flag) printf("%.*s\n", (int)req.body_size, (char *)req.body); http_req_free(&req); } while (!done); if (gssapi_done == 0) errx(1, "gssapi not done but http dance done"); return 0; }
uint32_t gp_export_ctx_id_to_gssx(uint32_t *min, int type, gss_OID mech, gss_ctx_id_t *in, gssx_ctx *out) { uint32_t ret_maj; uint32_t ret_min; gss_name_t src_name = GSS_C_NO_NAME; gss_name_t targ_name = GSS_C_NO_NAME; gss_buffer_desc export_buffer = GSS_C_EMPTY_BUFFER; gss_krb5_lucid_context_v1_t *lucid = NULL; uint32_t lifetime_rec; gss_OID mech_type; uint32_t ctx_flags; int is_locally_initiated; int is_open; int ret; /* we do not need the client to release anything until we handle state */ out->needs_release = false; ret_maj = gss_inquire_context(&ret_min, *in, &src_name, &targ_name, &lifetime_rec, &mech_type, &ctx_flags, &is_locally_initiated, &is_open); if (ret_maj) { if (type == EXP_CTX_PARTIAL) { /* This may happen on partially established context, * so just go on and put in what we can */ goto export; } goto done; } ret = gp_conv_oid_to_gssx(mech_type, &out->mech); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = gp_conv_name_to_gssx(&ret_min, src_name, &out->src_name); if (ret_maj) { goto done; } ret_maj = gp_conv_name_to_gssx(&ret_min, targ_name, &out->targ_name); if (ret_maj) { goto done; } out->lifetime = lifetime_rec; out->ctx_flags = ctx_flags; if (is_locally_initiated) { out->locally_initiated = true; } if (is_open) { out->open = true; } export: /* note: once converted the original context token is not usable anymore, * so this must be the last call to use it */ switch (type) {
int authenticate_gss_server_step(gss_server_state *state, const char *challenge) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_CONTINUE; // Always clear out the old response if (state->response != NULL) { free(state->response); state->response = NULL; } // If there is a challenge (data from the server) we need to give it to GSS if (challenge && *challenge) { size_t len; input_token.value = base64_decode(challenge, &len); input_token.length = len; } else { PyErr_SetString(KrbException_class, "No challenge parameter in request from client"); ret = AUTH_GSS_ERROR; goto end; } Py_BEGIN_ALLOW_THREADS maj_stat = gss_accept_sec_context(&min_stat, &state->context, state->server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &state->client_name, NULL, &output_token, (OM_uint32 *)&state->gss_flags, NULL, &state->client_creds); Py_END_ALLOW_THREADS if (GSS_ERROR(maj_stat)) { set_gss_error(maj_stat, min_stat); ret = AUTH_GSS_ERROR; goto end; } // Grab the server response to send back to the client if (output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);; maj_stat = gss_release_buffer(&min_stat, &output_token); } // Get the user name maj_stat = gss_display_name(&min_stat, state->client_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { set_gss_error(maj_stat, min_stat); ret = AUTH_GSS_ERROR; goto end; } state->username = (char *)malloc(output_token.length + 1); strncpy(state->username, (char*) output_token.value, output_token.length); state->username[output_token.length] = 0; // Get the target name if no server creds were supplied if (state->server_creds == GSS_C_NO_CREDENTIAL) { gss_name_t target_name = GSS_C_NO_NAME; maj_stat = gss_inquire_context(&min_stat, state->context, NULL, &target_name, NULL, NULL, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) { set_gss_error(maj_stat, min_stat); ret = AUTH_GSS_ERROR; goto end; } maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { set_gss_error(maj_stat, min_stat); ret = AUTH_GSS_ERROR; goto end; } state->targetname = (char *)malloc(output_token.length + 1); strncpy(state->targetname, (char*) output_token.value, output_token.length); state->targetname[output_token.length] = 0; } ret = AUTH_GSS_COMPLETE; end: if (output_token.length) gss_release_buffer(&min_stat, &output_token); if (input_token.value) free(input_token.value); return ret; }
OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_context ( OM_uint32 * minor_status, gss_const_ctx_id_t context_handle, gss_name_t * src_name, gss_name_t * targ_name, OM_uint32 * lifetime_rec, gss_OID * mech_type, OM_uint32 * ctx_flags, int * locally_initiated, int * open_context ) { gssspnego_ctx ctx; OM_uint32 maj_stat, junk; gss_name_t src_mn, targ_mn; *minor_status = 0; if (context_handle == GSS_C_NO_CONTEXT) return GSS_S_NO_CONTEXT; ctx = (gssspnego_ctx)context_handle; if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) return GSS_S_NO_CONTEXT; maj_stat = gss_inquire_context(minor_status, ctx->negotiated_ctx_id, &src_mn, &targ_mn, lifetime_rec, mech_type, ctx_flags, locally_initiated, open_context); if (maj_stat != GSS_S_COMPLETE) return maj_stat; if (src_name) { spnego_name name = calloc(1, sizeof(*name)); if (name == NULL) goto enomem; name->mech = src_mn; *src_name = (gss_name_t)name; } else gss_release_name(&junk, &src_mn); if (targ_name) { spnego_name name = calloc(1, sizeof(*name)); if (name == NULL) { gss_release_name(minor_status, src_name); goto enomem; } name->mech = targ_mn; *targ_name = (gss_name_t)name; } else gss_release_name(&junk, &targ_mn); return GSS_S_COMPLETE; enomem: gss_release_name(&junk, &targ_mn); gss_release_name(&junk, &src_mn); *minor_status = ENOMEM; return GSS_S_FAILURE; }
/* * Curl_sasl_create_gssapi_security_message() * * This is used to generate an already encoded GSSAPI (Kerberos V5) security * token message ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * chlg64 [in] - Pointer to the optional base64 encoded challenge message. * krb5 [in/out] - The gssapi data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, const char *chlg64, struct kerberos5data *krb5, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlglen = 0; size_t messagelen = 0; unsigned char *chlg = NULL; unsigned char *message = NULL; OM_uint32 gss_status; OM_uint32 gss_major_status; OM_uint32 gss_minor_status; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; unsigned int indata = 0; unsigned int outdata = 0; gss_qop_t qop = GSS_C_QOP_DEFAULT; unsigned int sec_layer = 0; unsigned int max_size = 0; gss_name_t username = GSS_C_NO_NAME; gss_buffer_desc username_token; /* Decode the base-64 encoded input message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "GSSAPI handshake failure (empty security message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Get the fully qualified username back from the context */ gss_major_status = gss_inquire_context(&gss_minor_status, krb5->context, &username, NULL, NULL, NULL, NULL, NULL, NULL); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, gss_minor_status, "gss_inquire_context() failed: "); Curl_safefree(chlg); return CURLE_OUT_OF_MEMORY; } /* Convert the username from internal format to a displayable token */ gss_major_status = gss_display_name(&gss_minor_status, username, &username_token, NULL); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, gss_minor_status, "gss_display_name() failed: "); Curl_safefree(chlg); return CURLE_OUT_OF_MEMORY; } /* Setup the challenge "input" security buffer */ input_token.value = chlg; input_token.length = chlglen; /* Decrypt the inbound challenge and obtain the qop */ gss_major_status = gss_unwrap(&gss_minor_status, krb5->context, &input_token, &output_token, NULL, &qop); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, gss_minor_status, "gss_unwrap() failed: "); gss_release_buffer(&gss_status, &username_token); Curl_safefree(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ if(output_token.length != 4) { infof(data, "GSSAPI handshake failure (invalid security data)\n"); gss_release_buffer(&gss_status, &username_token); Curl_safefree(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Copy the data out and free the challenge as it is not required anymore */ memcpy(&indata, output_token.value, 4); gss_release_buffer(&gss_status, &output_token); Curl_safefree(chlg); /* Extract the security layer */ sec_layer = indata & 0x000000FF; if(!(sec_layer & GSSAUTH_P_NONE)) { infof(data, "GSSAPI handshake failure (invalid security layer)\n"); gss_release_buffer(&gss_status, &username_token); return CURLE_BAD_CONTENT_ENCODING; } /* Extract the maximum message size the server can receive */ max_size = ntohl(indata & 0xFFFFFF00); if(max_size > 0) { /* The server has told us it supports a maximum receive buffer, however, as we don't require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } /* Allocate our message */ messagelen = sizeof(outdata) + username_token.length + 1; message = malloc(messagelen); if(!message) { gss_release_buffer(&gss_status, &username_token); return CURLE_OUT_OF_MEMORY; } /* Populate the message with the security layer, client supported receive message size and authorization identity including the 0x00 based terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization identity is not terminated with the zero-valued (%x00) octet." it seems necessary to include it. */ outdata = htonl(max_size) | sec_layer; memcpy(message, &outdata, sizeof(outdata)); memcpy(message + sizeof(outdata), username_token.value, username_token.length); message[messagelen - 1] = '\0'; /* Free the username token as it is not required anymore */ gss_release_buffer(&gss_status, &username_token); /* Setup the "authentication data" security buffer */ input_token.value = message; input_token.length = messagelen; /* Encrypt the data */ gss_major_status = gss_wrap(&gss_minor_status, krb5->context, 0, GSS_C_QOP_DEFAULT, &input_token, NULL, &output_token); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, gss_minor_status, "gss_wrap() failed: "); Curl_safefree(message); return CURLE_OUT_OF_MEMORY; } /* Base64 encode the response */ result = Curl_base64_encode(data, (char *) output_token.value, output_token.length, outptr, outlen); /* Free the output buffer */ gss_release_buffer(&gss_status, &output_token); /* Free the message buffer */ Curl_safefree(message); return result; }
int main(int argc, char **argv) { int i, s, done, print_body, gssapi_done, gssapi_started, optidx = 0; const char *host, *page; struct http_req req; char *headers[99]; /* XXX */ int num_headers; krb5_storage *sp; gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT; gss_name_t server = GSS_C_NO_NAME; gss_OID mech_oid, cred_mech_oid; OM_uint32 flags; OM_uint32 maj_stat, min_stat; setprogname(argv[0]); if(getarg(http_args, num_http_args, argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; mech_oid = select_mech(mech); if (cred_mech_str) cred_mech_oid = select_mech(cred_mech_str); else cred_mech_oid = mech_oid; if (argc != 1 && argc != 2) errx(1, "usage: %s host [page]", getprogname()); host = argv[0]; if (argc == 2) page = argv[1]; else page = "/"; flags = 0; if (delegate_flag) flags |= GSS_C_DELEG_FLAG; if (policy_flag) flags |= GSS_C_DELEG_POLICY_FLAG; if (mutual_flag) flags |= GSS_C_MUTUAL_FLAG; done = 0; num_headers = 0; gssapi_done = 0; gssapi_started = 0; if (client_str) { gss_buffer_desc name_buffer; gss_name_t name; gss_OID_set mechset = GSS_C_NO_OID_SET; name_buffer.value = client_str; name_buffer.length = strlen(client_str); maj_stat = gss_import_name(&min_stat, &name_buffer, GSS_C_NT_USER_NAME, &name); if (maj_stat) errx(1, "failed to import name"); if (cred_mech_oid) { gss_create_empty_oid_set(&min_stat, &mechset); gss_add_oid_set_member(&min_stat, cred_mech_oid, &mechset); } maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE, mechset, GSS_C_INITIATE, &client_cred, NULL, NULL); gss_release_name(&min_stat, &name); gss_release_oid_set(&min_stat, &mechset); if (maj_stat) errx(1, "failed to find cred of name %s", client_str); } { gss_buffer_desc name_token; char *name; asprintf(&name, "%s@%s", gss_service, host); name_token.length = strlen(name); name_token.value = name; maj_stat = gss_import_name(&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &server); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_inport_name: %s", name); free(name); } s = do_connect(host, port_str); if (s < 0) errx(1, "connection failed"); sp = krb5_storage_from_fd(s); if (sp == NULL) errx(1, "krb5_storage_from_fd"); do { print_body = 0; http_query(sp, host, page, headers, num_headers, &req); for (i = 0 ; i < num_headers; i++) free(headers[i]); num_headers = 0; if (strstr(req.response, " 200 ") != NULL) { print_body = 1; done = 1; } else if (strstr(req.response, " 401 ") != NULL) { if (http_find_header(&req, "WWW-Authenticate:") == NULL) errx(1, "Got %s but missed `WWW-Authenticate'", req.response); } if (!gssapi_done) { const char *h = http_find_header(&req, "WWW-Authenticate:"); if (h == NULL) errx(1, "Got %s but missed `WWW-Authenticate'", req.response); if (strncasecmp(h, "Negotiate", 9) == 0) { gss_buffer_desc input_token, output_token; if (verbose_flag) printf("Negotiate found\n"); i = 9; while(h[i] && isspace((unsigned char)h[i])) i++; if (h[i] != '\0') { size_t len = strlen(&h[i]); int slen; if (len == 0) errx(1, "invalid Negotiate token"); input_token.value = emalloc(len); slen = base64_decode(&h[i], input_token.value); if (slen < 0) errx(1, "invalid base64 Negotiate token %s", &h[i]); input_token.length = slen; } else { if (gssapi_started) errx(1, "Negotiate already started"); gssapi_started = 1; input_token.length = 0; input_token.value = NULL; } if (strstr(req.response, " 200 ") != NULL) sleep(1); maj_stat = gss_init_sec_context(&min_stat, client_cred, &context_hdl, server, mech_oid, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); if (maj_stat == GSS_S_CONTINUE_NEEDED) { } else if (maj_stat == GSS_S_COMPLETE) { gss_name_t targ_name, src_name; gss_buffer_desc name_buffer; gss_OID mech_type; gssapi_done = 1; maj_stat = gss_inquire_context(&min_stat, context_hdl, &src_name, &targ_name, NULL, &mech_type, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_inquire_context"); printf("Negotiate done: %s\n", mech); maj_stat = gss_display_name(&min_stat, src_name, &name_buffer, NULL); if (GSS_ERROR(maj_stat)) gss_print_errors(min_stat); else printf("Source: %.*s\n", (int)name_buffer.length, (char *)name_buffer.value); gss_release_buffer(&min_stat, &name_buffer); maj_stat = gss_display_name(&min_stat, targ_name, &name_buffer, NULL); if (GSS_ERROR(maj_stat)) gss_print_errors(min_stat); else printf("Target: %.*s\n", (int)name_buffer.length, (char *)name_buffer.value); gss_release_name(&min_stat, &targ_name); gss_release_buffer(&min_stat, &name_buffer); } else { gss_err (1, min_stat, "gss_init_sec_context"); } if (output_token.length) { char *neg_token; base64_encode(output_token.value, (int)output_token.length, &neg_token); asprintf(&headers[0], "Authorization: Negotiate %s", neg_token); num_headers = 1; free(neg_token); gss_release_buffer(&min_stat, &output_token); } if (input_token.length) free(input_token.value); } else done = 1; } else done = 1; if (print_body || verbose_flag) printf("%.*s\n", (int)req.body_size, (char *)req.body); http_req_free(&req); } while (!done); if (gssapi_done == 0) errx(1, "gssapi not done but http dance done"); krb5_storage_free(sp); close(s); return 0; }
/* * this code uses the userland rpcsec gss library to create a krb5 * context on behalf of the kernel */ static void process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, char *service) { CLIENT *rpc_clnt = NULL; AUTH *auth = NULL; struct authgss_private_data pd; gss_buffer_desc token; int err, downcall_err = -EACCES; OM_uint32 maj_stat, min_stat, lifetime_rec; pid_t pid, childpid = -1; gss_name_t gacceptor = GSS_C_NO_NAME; gss_OID mech; gss_buffer_desc acceptor = {0}; token.length = 0; token.value = NULL; memset(&pd, 0, sizeof(struct authgss_private_data)); /* * If "service" is specified, then the kernel is indicating that * we must use machine credentials for this request. (Regardless * of the uid value or the setting of root_uses_machine_creds.) * If the service value is "*", then any service name can be used. * Otherwise, it specifies the service name that should be used. * (For now, the values of service will only be "*" or "nfs".) * * Restricting gssd to use "nfs" service name is needed for when * the NFS server is doing a callback to the NFS client. In this * case, the NFS server has to authenticate itself as "nfs" -- * even if there are other service keys such as "host" or "root" * in the keytab. * * Another case when the kernel may specify the service attribute * is when gssd is being asked to create the context for a * SETCLIENT_ID operation. In this case, machine credentials * must be used for the authentication. However, the service name * used for this case is not important. * */ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && service == NULL)) { /* already running as uid 0 */ if (uid == 0) goto no_fork; pid = fork(); switch(pid) { case 0: /* Child: fall through to rest of function */ childpid = getpid(); unsetenv("KRB5CCNAME"); printerr(2, "CHILD forked pid %d \n", childpid); break; case -1: /* fork() failed! */ printerr(0, "WARNING: unable to fork() to handle" "upcall: %s\n", strerror(errno)); return; default: /* Parent: just wait on child to exit and return */ do { pid = wait(&err); } while(pid == -1 && errno != -ECHILD); if (WIFSIGNALED(err)) printerr(0, "WARNING: forked child was killed" "with signal %d\n", WTERMSIG(err)); return; } no_fork: auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err, &err, &rpc_clnt); if (err) goto out_return_error; } if (auth == NULL) { if (uid == 0 && (root_uses_machine_creds == 1 || service != NULL)) { auth = krb5_use_machine_creds(clp, uid, tgtname, service, &rpc_clnt); if (auth == NULL) goto out_return_error; } else { /* krb5_not_machine_creds logs the error */ goto out_return_error; } } if (!authgss_get_private_data(auth, &pd)) { printerr(1, "WARNING: Failed to obtain authentication " "data for user with uid %d for server %s\n", uid, clp->servername); goto out_return_error; } /* Grab the context lifetime and acceptor name out of the ctx. */ maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor, &lifetime_rec, &mech, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE) { printerr(1, "WARNING: Failed to inquire context " "maj_stat (0x%x)\n", maj_stat); lifetime_rec = 0; } else { get_hostbased_client_buffer(gacceptor, mech, &acceptor); gss_release_name(&min_stat, &gacceptor); } /* * The serialization can mean turning pd.pd_ctx into a lucid context. If * that happens then the pd.pd_ctx will be unusable, so we must never * try to use it after this point. */ if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) { printerr(1, "WARNING: Failed to serialize krb5 context for " "user with uid %d for server %s\n", uid, clp->servername); goto out_return_error; } do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor); out: gss_release_buffer(&min_stat, &acceptor); if (token.value) free(token.value); #ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA if (pd.pd_ctx_hndl.length != 0 || pd.pd_ctx != 0) authgss_free_private_data(&pd); #endif if (auth) AUTH_DESTROY(auth); if (rpc_clnt) clnt_destroy(rpc_clnt); pid = getpid(); if (pid == childpid) exit(0); else return; out_return_error: do_error_downcall(fd, uid, downcall_err); goto out; }