2 * Copyright (c) 2011, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of JANET(UK) nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
33 * This code was adapted from the MIT Kerberos Consortium's
34 * GSS example code, which was distributed under the following
37 * Copyright 2004-2006 Massachusetts Institute of Technology.
38 * All Rights Reserved.
40 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
41 * distribute this software and its documentation for any purpose and
42 * without fee is hereby granted, provided that the above copyright
43 * notice appear in all copies and that both that copyright notice and
44 * this permission notice appear in supporting documentation, and that
45 * the name of M.I.T. not be used in advertising or publicity pertaining
46 * to distribution of the software without specific, written prior
47 * permission. Furthermore if you modify this software you must label
48 * your software as modified software and not distribute it in such a
49 * fashion that it might be confused with the original M.I.T. software.
50 * M.I.T. makes no representations about the suitability of
51 * this software for any purpose. It is provided "as is" without express
52 * or implied warranty.
57 /* --------------------------------------------------------------------------- */
59 static int gsscon_connect (const char *inHost, int inPort, int *outFD)
63 struct hostent *hp = NULL;
64 struct sockaddr_in saddr;
67 hp = gethostbyname (inHost);
68 if (hp == NULL) { err = errno; }
72 saddr.sin_family = hp->h_addrtype;
73 memcpy ((char *) &saddr.sin_addr, hp->h_addr, sizeof (saddr.sin_addr));
74 saddr.sin_port = htons(inPort);
76 fd = socket (AF_INET, SOCK_STREAM, 0);
77 if (fd < 0) { err = errno; }
81 err = connect (fd, (struct sockaddr *) &saddr, sizeof (saddr));
82 if (err < 0) { err = errno; }
86 printf ("connecting to host '%s' on port %d\n", inHost, inPort);
88 fd = -1; /* takes ownership */
90 printError (err, "OpenConnection failed");
93 if (fd >= 0) { close (fd); }
98 /* --------------------------------------------------------------------------- */
100 static int gsscon_active_authenticate (int inSocket,
101 const char *inClientName,
102 const char *inServiceName,
103 gss_ctx_id_t *outGSSContext)
106 OM_uint32 majorStatus;
107 OM_uint32 minorStatus = 0;
108 gss_name_t serviceName = NULL;
109 gss_name_t clientName = NULL;
110 gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL;
111 gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
112 OM_uint32 actualFlags = 0;
114 char *inputTokenBuffer = NULL;
115 size_t inputTokenBufferLength = 0;
116 gss_buffer_desc inputToken; /* buffer received from the server */
117 gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER;
119 if (inSocket < 0 ) { err = EINVAL; }
120 if (!inServiceName) { err = EINVAL; }
121 if (!outGSSContext) { err = EINVAL; }
124 * Here is where the client picks the client principal it wants to use.
125 * If your application knows what the user's client principal should be,
126 * it should set that here. Otherwise leave clientCredentials set to NULL
127 * and Kerberos will attempt to use the client principal in the default
132 if (inClientName != NULL) {
133 gss_buffer_desc nameBuffer = { strlen (inClientName), (char *) inClientName };
135 majorStatus = gss_import_name (&minorStatus, &nameBuffer, GSS_C_NT_USER_NAME, &clientName);
136 if (majorStatus != GSS_S_COMPLETE) {
137 printGSSErrors ("gss_import_name(inClientName)", majorStatus, minorStatus);
138 err = minorStatus ? minorStatus : majorStatus;
142 majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
143 GSS_C_INITIATE, &clientCredentials, NULL, NULL);
144 if (majorStatus != GSS_S_COMPLETE) {
145 printGSSErrors ("gss_acquire_cred", majorStatus, minorStatus);
146 err = minorStatus ? minorStatus : majorStatus;
153 * Here is where the client picks the service principal it will try to use to
154 * connect to the server. In the case of the gssClientSample, the service
155 * principal is passed in on the command line, however, in a real world example,
156 * this would be unacceptable from a user interface standpoint since the user
157 * shouldn't need to know the server's service principal.
159 * In traditional Kerberos setups, the service principal would be constructed from
160 * the type of the service (eg: "imap"), the DNS hostname of the server
161 * (eg: "mailserver.domain.com") and the client's local realm (eg: "DOMAIN.COM")
162 * to form a full principal string (eg: "imap/mailserver.domain.com@DOMAIN.COM").
164 * Now that many sites do not have DNS, this setup is becoming less common.
165 * However you decide to generate the service principal, you need to adhere
166 * to the following constraint: The service principal must be constructed
167 * by the client, typed in by the user or administrator, or transmitted to
168 * the client in a secure manner from a trusted third party -- such as
169 * through an encrypted connection to a directory server. You should not
170 * have the server send the client the service principal name as part of
171 * the authentication negotiation.
173 * The reason you can't let the server tell the client which principal to
174 * use is that many machines at a site will have their own service principal
175 * and keytab which identifies the machine -- in a Windows Active Directory
176 * environment all machines have a service principal and keytab. Some of these
177 * machines (such as a financial services server) will be more trustworthy than
178 * others (such as a random machine on a coworker's desk). If the owner of
179 * one of these untrustworthy machines can trick the client into using the
180 * untrustworthy machine's principal instead of the financial services
181 * server's principal, then he can trick the client into authenticating
182 * and connecting to the untrustworthy machine. The untrustworthy machine can
183 * then harvest any confidential information the client sends to it, such as
184 * credit card information or social security numbers.
186 * If your protocol already involves sending the service principal as part of
187 * your authentication negotiation, your client should cache the name it gets
188 * after the first successful authentication so that the problem above can
189 * only happen on the first connection attempt -- similar to what ssh does with
194 gss_buffer_desc nameBuffer = { strlen (inServiceName), (char *) inServiceName };
196 majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME, &serviceName);
197 if (majorStatus != GSS_S_COMPLETE) {
198 printGSSErrors ("gss_import_name(inServiceName)", majorStatus, minorStatus);
199 err = minorStatus ? minorStatus : majorStatus;
204 * The main authentication loop:
206 * GSS is a multimechanism API. Because the number of packet exchanges required to
207 * authenticate can vary between mechanisms, we need to loop calling
208 * gss_init_sec_context, passing the "input tokens" received from the server and
209 * send the resulting "output tokens" back until we get GSS_S_COMPLETE or an error.
212 majorStatus = GSS_S_CONTINUE_NEEDED;
214 gss_OID_desc EAP_OID = { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x11" };
216 while (!err && (majorStatus != GSS_S_COMPLETE)) {
217 gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */
218 OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG |
219 GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
221 printf ("Calling gss_init_sec_context...\n");
222 majorStatus = gss_init_sec_context (&minorStatus,
226 &EAP_OID /* mech_type */,
229 GSS_C_NO_CHANNEL_BINDINGS,
231 NULL /* actual_mech_type */,
234 NULL /* time_rec */);
236 /* Send the output token to the server (even on error) */
237 if ((outputToken.length > 0) && (outputToken.value != NULL)) {
238 err = WriteToken (inSocket, outputToken.value, outputToken.length);
240 /* free the output token */
241 gss_release_buffer (&minorStatus, &outputToken);
245 if (majorStatus == GSS_S_CONTINUE_NEEDED) {
246 /* Protocol requires another packet exchange */
248 /* Clean up old input buffer */
249 if (inputTokenBuffer) {
250 free (inputTokenBuffer);
251 inputTokenBuffer = NULL; /* don't double-free */
254 /* Read another input token from the server */
255 err = ReadToken (inSocket, &inputTokenBuffer, &inputTokenBufferLength);
258 /* Set up input buffers for the next run through the loop */
259 inputToken.value = inputTokenBuffer;
260 inputToken.length = inputTokenBufferLength;
261 inputTokenPtr = &inputToken;
263 } else if (majorStatus != GSS_S_COMPLETE) {
264 printGSSErrors ("gss_init_sec_context", majorStatus, minorStatus);
265 err = minorStatus ? minorStatus : majorStatus;
271 *outGSSContext = gssContext;
274 printError (err, "AuthenticateToServer failed");
277 if (inputTokenBuffer) { free (inputTokenBuffer); }
278 if (serviceName ) { gss_release_name (&minorStatus, &serviceName); }
279 if (clientName ) { gss_release_name (&minorStatus, &clientName); }
281 if (clientCredentials != GSS_C_NO_CREDENTIAL) {
282 gss_release_cred (&minorStatus, &clientCredentials); }
283 if (gssContext != GSS_C_NO_CONTEXT) {
284 gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); }