* or implied warranty.
*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+
#include <gsscon.h>
-/* --------------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------------
+ */
-int gsscon_connect (const char *inHost, int inPort, int *outFD)
+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;
- struct hostent *hp = NULL;
- struct sockaddr_in saddr;
-
- if (!err) {
- hp = gethostbyname (inHost);
- if (hp == NULL) { err = errno; }
- }
-
- if (!err) {
- saddr.sin_family = hp->h_addrtype;
- memcpy ((char *) &saddr.sin_addr, hp->h_addr, sizeof (saddr.sin_addr));
- saddr.sin_port = htons(inPort);
-
- fd = socket (AF_INET, SOCK_STREAM, 0);
- if (fd < 0) { err = errno; }
- }
-
- if (!err) {
- err = connect (fd, (struct sockaddr *) &saddr, sizeof (saddr));
- if (err < 0) { err = errno; }
- }
-
- if (!err) {
- printf ("connecting to host '%s' on port %d\n", inHost, inPort);
- *outFD = fd;
- fd = -1; /* takes ownership */
- } else {
- gsscon_print_error (err, "OpenConnection failed");
- }
+ 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_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;
+ int len = 0;
+
+ if (!inServiceName) { err = EINVAL; }
+ if (!outGSSContext) { err = EINVAL; }
- if (fd >= 0) { close (fd); }
+ 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;
+ }
- return err;
-}
+ 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;
+ }
-/* --------------------------------------------------------------------------- */
-
-int gsscon_active_authenticate (int inSocket,
- const char *inClientName,
- const char *inServiceName,
- gss_ctx_id_t *outGSSContext)
-{
- int err = 0;
- OM_uint32 majorStatus;
- OM_uint32 minorStatus = 0;
- 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;
+ 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;
+ }
+ }
- char *inputTokenBuffer = NULL;
- size_t inputTokenBufferLength = 0;
- gss_buffer_desc inputToken; /* buffer received from the server */
- gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER;
+ if (fd==-1)
+ err=1;
+ }
- if (inSocket < 0 ) { err = EINVAL; }
- if (!inServiceName) { err = EINVAL; }
- if (!outGSSContext) { err = EINVAL; }
+ if (!err) {
+ *outFD = fd;
+ fd = -1; /* takes ownership */
+ } else {
+ gsscon_print_error (err, "OpenConnection failed");
+ }
- /*
- * Here is where the client picks the client principal it wants to use.
- * If your application knows what the user's client principal should be,
- * it should set that here. Otherwise leave clientCredentials set to NULL
- * and Kerberos will attempt to use the client principal in the default
- * ccache.
- */
-
- if (!err) {
- if (inClientName != NULL) {
- gss_buffer_desc nameBuffer = { strlen (inClientName), (char *) inClientName };
-
- majorStatus = gss_import_name (&minorStatus, &nameBuffer, GSS_C_NT_USER_NAME, &clientName);
- if (majorStatus != GSS_S_COMPLETE) {
- gsscon_print_gss_errors ("gss_import_name(inClientName)", majorStatus, minorStatus);
- err = minorStatus ? minorStatus : majorStatus;
- }
-
- 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;
- }
- }
- }
+ if (fd >= 0) { close (fd); }
+
+ if (!err) {
+ majorStatus = gss_acquire_cred (&minorStatus, GSS_C_NO_NAME, 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/mailserver.domain.com@DOMAIN.COM").
- *
- * 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.
- */
+ /*
+ * 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/mailserver.domain.com@DOMAIN.COM").
+ *
+ * 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) {
- gss_buffer_desc nameBuffer = { strlen (inServiceName), (char *) inServiceName };
-
- majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME, &serviceName);
- if (majorStatus != GSS_S_COMPLETE) {
- gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus);
- err = minorStatus ? minorStatus : majorStatus;
- }
+ if (!err) {
+ len = asprintf(&name, "%s@%s", inServiceName, inHost);
+ if (len < 0) {
+ /* asprintf failed, pick an error to return... */
+ err = GSS_S_BAD_NAME;
+ } else {
+ nameBuffer.length = (size_t) len;
+ 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;
+ }
+
+ /* free the input name and null pointers to avoid reuse */
+ free(name);
+ name = NULL;
+ nameBuffer.value = NULL;
}
-
- /*
- * 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;
+ /*
+ * 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.
+ */
- gss_OID_desc EAP_OID = { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x11" };
+ 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);
+ 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);
- printf ("Calling gss_init_sec_context...\n");
- 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 */);
+ 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 (inSocket, outputToken.value, outputToken.length);
+ /* 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 (&minorStatus, &outputToken);
- }
+ /* free the output token */
+ gss_release_buffer (&minorStatusToo, &outputToken);
+ }
- if (!err) {
- if (majorStatus == GSS_S_CONTINUE_NEEDED) {
- /* Protocol requires another packet exchange */
+ 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 */
- }
+ /* 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 (inSocket, &inputTokenBuffer, &inputTokenBufferLength);
+ /* 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) {
+ /* 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 (!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 (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); }
+ if (inputTokenBuffer) { free (inputTokenBuffer); }
+ if (serviceName ) { gss_release_name (&minorStatus, &serviceName); }
+ 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;
+ return err;
}