int gsscon_write_token (int inSocket, const char *inTokenValue, size_t inTokenLength) { int err = 0; u_int32_t tokenLength = htonl (inTokenLength); if (!inTokenValue) { err = EINVAL; } if (!err) { err = WriteBuffer (inSocket, (char *) &tokenLength, 4); } if (!err) { err = WriteBuffer (inSocket, inTokenValue, inTokenLength); } if (!err) { // printf ("Wrote token:\n"); // PrintBuffer (inTokenValue, inTokenLength); } else { gsscon_print_error (err, "gsscon_write_token() failed"); } return err; }
static int ReadBuffer (int inSocket, size_t inBufferLength, char *ioBuffer) { int err = 0; ssize_t bytesRead = 0; if (!ioBuffer) { err = EINVAL; } if (!err) { char *ptr = ioBuffer; do { ssize_t count = read (inSocket, ptr, inBufferLength - bytesRead); if (count < 0) { /* Try again on EINTR */ if (errno != EINTR) { err = errno; } } else if (count == 0) { err = ECONNRESET; /* EOF and we expected data */ } else { ptr += count; bytesRead += count; } } while (!err && (bytesRead < inBufferLength)); } if (err) { gsscon_print_error (err, "ReadBuffer failed"); } return err; }
static int WriteBuffer (int inSocket, const char *inBuffer, size_t inBufferLength) { int err = 0; ssize_t bytesWritten = 0; if (!inBuffer) { err = EINVAL; } if (!err) { const char *ptr = inBuffer; do { ssize_t count; count = write (inSocket, ptr, inBufferLength - bytesWritten); if (count < 0) { /* Try again on EINTR */ if (errno != EINTR) { err = errno; } } else { ptr += count; bytesWritten += count; } } while (!err && (bytesWritten < inBufferLength)); } if (err) { gsscon_print_error (err, "WriteBuffer failed"); } return err; }
int gsscon_write_encrypted_token (int inSocket, const gss_ctx_id_t inContext, const char *inToken, size_t inTokenLength) { int err = 0; OM_uint32 majorStatus; OM_uint32 minorStatus = 0; gss_buffer_desc outputBuffer = { 0, NULL }; if (!inContext) { err = EINVAL; } if (!inToken ) { err = EINVAL; } if (!err) { gss_buffer_desc inputBuffer = { inTokenLength, (char *) inToken }; int encrypt = 1; /* do encryption and integrity protection */ int encrypted = 0; /* did mechanism encrypt/integrity protect? */ majorStatus = gss_wrap (&minorStatus, inContext, encrypt, GSS_C_QOP_DEFAULT, &inputBuffer, &encrypted, &outputBuffer); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_wrap", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } else if (!encrypted) { fprintf (stderr, "WARNING! Mechanism does not support encryption!"); err = EINVAL; /* You may not want to fail here. */ } } if (!err) { // printf ("Unencrypted token:\n"); // PrintBuffer (inToken, inTokenLength); err = gsscon_write_token (inSocket, outputBuffer.value, outputBuffer.length); } if (!err) { } else { gsscon_print_error (err, "gsscon_write_token failed"); } if (outputBuffer.value) { gss_release_buffer (&minorStatus, &outputBuffer); } return err; }
int gsscon_read_token (int inSocket, char **outTokenValue, size_t *outTokenLength) { int err = 0; char *token = NULL; u_int32_t tokenLength = 0; if (!outTokenValue ) { err = EINVAL; } if (!outTokenLength) { err = EINVAL; } if (!err) { err = ReadBuffer (inSocket, 4, (char *) &tokenLength); } if (!err) { tokenLength = ntohl (tokenLength); token = malloc (tokenLength); if (token==NULL) { err=EIO; } else { memset (token, 0, tokenLength); err = ReadBuffer (inSocket, tokenLength, token); } } if (!err) { *outTokenLength = tokenLength; *outTokenValue = token; token = NULL; /* only free on error */ } else { gsscon_print_error (err, "ReadToken failed"); } if (token) { free (token); } return err; }
int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServiceName, int *outFD, gss_ctx_id_t *outGSSContext) { int err = 0; int fd = -1; OM_uint32 majorStatus; OM_uint32 minorStatus = 0, minorStatusToo = 0; struct addrinfo *ai=NULL; struct addrinfo *ai_head=NULL; struct addrinfo hints={.ai_family=AF_UNSPEC, .ai_socktype=SOCK_STREAM, .ai_protocol=IPPROTO_TCP}; struct sockaddr_in saddr; char *port=NULL; gss_name_t serviceName = NULL; gss_name_t clientName = NULL; gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL; gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; OM_uint32 actualFlags = 0; char *inputTokenBuffer = NULL; size_t inputTokenBufferLength = 0; gss_buffer_desc inputToken; /* buffer received from the server */ gss_buffer_desc nameBuffer; gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER; char *name; if (!inServiceName) { err = EINVAL; } if (!outGSSContext) { err = EINVAL; } if (!err) { /* get a string for getaddrinfo */ if (asprintf(&port, "%d", inPort)>0) { err=getaddrinfo(inHost, port, &hints, &ai_head); free(port); } else err=1; } if (!err) { /* try all options returned until one works */ for (ai=ai_head,fd=-1; (ai!=NULL) && (fd==-1); ai=ai->ai_next) { fd=socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (fd < 0) { fd=-1; continue; } fprintf(stderr, "gss_connect: Connecting to host '%s' on port %d\n", inHost, inPort); err=connect(fd, ai->ai_addr, ai->ai_addrlen); if (err!=0) { close(fd); fd=-1; continue; } } if (fd==-1) err=1; } if (!err) { *outFD = fd; fd = -1; /* takes ownership */ } else { gsscon_print_error (err, "OpenConnection failed"); } if (fd >= 0) { close (fd); } if (!err) { majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &clientCredentials, NULL, NULL); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } /* * Here is where the client picks the service principal it will * try to use to connect to the server. In the case of the * gssClientSample, the service principal is passed in on the * command line, however, in a real world example, this would be * unacceptable from a user interface standpoint since the user * shouldn't need to know the server's service principal. * * In traditional Kerberos setups, the service principal would be * constructed from the type of the service (eg: "imap"), the DNS * hostname of the server (eg: "mailserver.domain.com") and the * client's local realm (eg: "DOMAIN.COM") to form a full * principal string (eg: "imap/[email protected]"). * * Now that many sites do not have DNS, this setup is becoming * less common. However you decide to generate the service * principal, you need to adhere to the following constraint: The * service principal must be constructed by the client, typed in * by the user or administrator, or transmitted to the client in a * secure manner from a trusted third party -- such as through an * encrypted connection to a directory server. You should not * have the server send the client the service principal name as * part of the authentication negotiation. * * The reason you can't let the server tell the client which * principal to use is that many machines at a site will have * their own service principal and keytab which identifies the * machine -- in a Windows Active Directory environment all * machines have a service principal and keytab. Some of these * machines (such as a financial services server) will be more * trustworthy than others (such as a random machine on a * coworker's desk). If the owner of one of these untrustworthy * machines can trick the client into using the untrustworthy * machine's principal instead of the financial services server's * principal, then he can trick the client into authenticating and * connecting to the untrustworthy machine. The untrustworthy * machine can then harvest any confidential information the * client sends to it, such as credit card information or social * security numbers. * * If your protocol already involves sending the service principal * as part of your authentication negotiation, your client should * cache the name it gets after the first successful * authentication so that the problem above can only happen on the * first connection attempt -- similar to what ssh does with host * keys. */ if (!err) { nameBuffer.length = asprintf(&name, "%s@%s", inServiceName, inHost); nameBuffer.value = name; majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } /* * The main authentication loop: * * GSS is a multimechanism API. Because the number of packet * exchanges required to authenticate can vary between mechanisms, * we need to loop calling gss_init_sec_context, passing the * "input tokens" received from the server and send the resulting * "output tokens" back until we get GSS_S_COMPLETE or an error. */ majorStatus = GSS_S_CONTINUE_NEEDED; gss_OID_desc EAP_OID = { 9, "\x2B\x06\x01\x05\x05\x0F\x01\x01\x11" }; while (!err && (majorStatus != GSS_S_COMPLETE)) { gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */ OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG); majorStatus = gss_init_sec_context (&minorStatus, clientCredentials, &gssContext, serviceName, &EAP_OID /* mech_type */, requestedFlags, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, inputTokenPtr, NULL /* actual_mech_type */, &outputToken, &actualFlags, NULL /* time_rec */); /* Send the output token to the server (even on error) */ if ((outputToken.length > 0) && (outputToken.value != NULL)) { err = gsscon_write_token (*outFD, outputToken.value, outputToken.length); /* free the output token */ gss_release_buffer (&minorStatusToo, &outputToken); } if (!err) { if (majorStatus == GSS_S_CONTINUE_NEEDED) { /* Protocol requires another packet exchange */ /* Clean up old input buffer */ if (inputTokenBuffer) { free (inputTokenBuffer); inputTokenBuffer = NULL; /* don't double-free */ } /* Read another input token from the server */ err = gsscon_read_token (*outFD, &inputTokenBuffer, &inputTokenBufferLength); if (!err) { /* Set up input buffers for the next run through the loop */ inputToken.value = inputTokenBuffer; inputToken.length = inputTokenBufferLength; inputTokenPtr = &inputToken; } } else if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_init_sec_context", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } } if (!err) { *outGSSContext = gssContext; gssContext = NULL; } else { gsscon_print_error (err, "AuthenticateToServer failed"); } if (inputTokenBuffer) { free (inputTokenBuffer); } if (serviceName ) { gss_release_name (&minorStatus, &serviceName); } if (clientName ) { gss_release_name (&minorStatus, &clientName); } if (ai_head ) { freeaddrinfo(ai_head); } if (clientCredentials != GSS_C_NO_CREDENTIAL) { gss_release_cred (&minorStatus, &clientCredentials); } if (gssContext != GSS_C_NO_CONTEXT) { gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } return err; }
int gsscon_read_encrypted_token (int inSocket, const gss_ctx_id_t inContext, char **outTokenValue, size_t *outTokenLength) { int err = 0; char *token = NULL; size_t tokenLength = 0; OM_uint32 majorStatus; OM_uint32 minorStatus = 0; gss_buffer_desc outputBuffer = { 0 , NULL}; char *unencryptedToken = NULL; if (!inContext ) { err = EINVAL; } if (!outTokenValue ) { err = EINVAL; } if (!outTokenLength) { err = EINVAL; } if (!err) { err = gsscon_read_token (inSocket, &token, &tokenLength); } if (!err) { gss_buffer_desc inputBuffer = { tokenLength, token}; int encrypted = 0; /* did mechanism encrypt/integrity protect? */ majorStatus = gss_unwrap (&minorStatus, inContext, &inputBuffer, &outputBuffer, &encrypted, NULL /* qop_state */); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors("gss_unwrap", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } else if (!encrypted) { fprintf (stderr, "WARNING! Mechanism not using encryption!"); err = EINVAL; /* You may not want to fail here. */ } } if (!err) { unencryptedToken = malloc (outputBuffer.length); if (unencryptedToken == NULL) { err = ENOMEM; } } if (!err) { memcpy (unencryptedToken, outputBuffer.value, outputBuffer.length); // printf ("Unencrypted token:\n"); // PrintBuffer (unencryptedToken, outputBuffer.length); *outTokenLength = outputBuffer.length; *outTokenValue = unencryptedToken; unencryptedToken = NULL; /* only free on error */ } else { gsscon_print_error (err, "ReadToken failed"); } if (token ) { free (token); } if (outputBuffer.value) { gss_release_buffer (&minorStatus, &outputBuffer); } if (unencryptedToken ) { free (unencryptedToken); } return err; }
int gsscon_passive_authenticate (int inSocket, gss_buffer_desc inNameBuffer, gss_ctx_id_t *outGSSContext, client_cb_fn clientCb, void *clientCbData) { int err = 0; OM_uint32 majorStatus; OM_uint32 minorStatus = 0, minorStatusToo = 0; gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; gss_name_t clientName = GSS_C_NO_NAME, serviceName = GSS_C_NO_NAME; gss_cred_id_t acceptorCredentials = NULL; gss_buffer_desc clientDisplayName = {0, NULL}; char *inputTokenBuffer = NULL; size_t inputTokenBufferLength = 0; gss_buffer_desc inputToken; /* buffer received from the server */ printf("In gsscon_passive_authenticate(), inNameBuffer = %s\n", inNameBuffer.value); if (inSocket < 0 ) { err = EINVAL; } if (!outGSSContext) { err = EINVAL; } if (!err) majorStatus = gss_import_name (&minorStatus, &inNameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_import_name(serviceName)", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } if (!err) { majorStatus = gss_acquire_cred ( &minorStatus, serviceName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &acceptorCredentials, NULL /*mechs out*/, NULL /*time out*/); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } /* * The main authentication loop: * * GSS is a multimechanism API. The number of packet exchanges required to * authenticatevaries between mechanisms. As a result, we need to loop reading * input tokens from the client, calling gss_accept_sec_context on the input * tokens and send the resulting output tokens back to the client until we * get GSS_S_COMPLETE or an error. * * When we are done, save the client principal so we can make authorization * checks. */ majorStatus = GSS_S_CONTINUE_NEEDED; while (!err && (majorStatus != GSS_S_COMPLETE)) { /* Clean up old input buffer */ if (inputTokenBuffer != NULL) { free (inputTokenBuffer); inputTokenBuffer = NULL; /* don't double-free */ } err = gsscon_read_token (inSocket, &inputTokenBuffer, &inputTokenBufferLength); if (!err) { /* Set up input buffers for the next run through the loop */ inputToken.value = inputTokenBuffer; inputToken.length = inputTokenBufferLength; } if (!err) { /* buffer to send to the server */ gss_buffer_desc outputToken = { 0, NULL }; /* * accept_sec_context does the actual work of taking the client's * request and generating an appropriate reply. */ majorStatus = gss_accept_sec_context (&minorStatus, &gssContext, acceptorCredentials, &inputToken, GSS_C_NO_CHANNEL_BINDINGS, &clientName, NULL /* actual_mech_type */, &outputToken, NULL /* req_flags */, NULL /* time_rec */, NULL /* delegated_cred_handle */); if ((outputToken.length > 0) && (outputToken.value != NULL)) { /* Send the output token to the client (even on error) */ err = gsscon_write_token (inSocket, outputToken.value, outputToken.length); /* free the output token */ gss_release_buffer (&minorStatusToo, &outputToken); } } if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) { gsscon_print_gss_errors ("gss_accept_sec_context", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } if (!err) { majorStatus = gss_display_name(&minorStatus, clientName, &clientDisplayName, NULL); if (GSS_ERROR(majorStatus)) { gsscon_print_gss_errors("gss_display_name", majorStatus, minorStatus); err = EINVAL; } if (!err) err = clientCb(clientName, &clientDisplayName, clientCbData); } if (!err) { *outGSSContext = gssContext; gssContext = NULL; } else { gsscon_print_error (err, "Authenticate failed"); } if (inputTokenBuffer) { free (inputTokenBuffer); } if (gssContext != GSS_C_NO_CONTEXT) { gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } if (clientName != GSS_C_NO_NAME) gss_release_name(&minorStatus, &clientName); if (clientDisplayName.value != NULL) gss_release_buffer(&minorStatus, &clientDisplayName); gss_release_name( &minorStatus, &serviceName); gss_release_cred( &minorStatus, &acceptorCredentials); return err; }