1/* 2 * gssServerSample.c - gssSample server program 3 * 4 * Copyright 2004-2005 Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 8 * distribute this software and its documentation for any purpose and 9 * without fee is hereby granted, provided that the above copyright 10 * notice appear in all copies and that both that copyright notice and 11 * this permission notice appear in supporting documentation, and that 12 * the name of M.I.T. not be used in advertising or publicity pertaining 13 * to distribution of the software without specific, written prior 14 * permission. Furthermore if you modify this software you must label 15 * your software as modified software and not distribute it in such a 16 * fashion that it might be confused with the original M.I.T. software. 17 * M.I.T. makes no representations about the suitability of 18 * this software for any purpose. It is provided "as is" without express 19 * or implied warranty. 20 */ 21 22 23#include <sys/types.h> 24#include <stdio.h> 25#include <unistd.h> 26#include <string.h> 27#include <errno.h> 28#include <sys/socket.h> 29#include <netinet/in.h> 30#include <Kerberos/Kerberos.h> 31#include "test-gss-common.h" 32 33const char *gServiceName = NULL; 34 35/* --------------------------------------------------------------------------- */ 36 37static int SetupListeningSocket (int inPort, int *outFD) 38{ 39 int err = 0; 40 int fd = -1; 41 42 if (!err) { 43 fd = socket (AF_INET, SOCK_STREAM, 0); 44 if (fd < 0) { err = errno; } 45 } 46 47 if (!err) { 48 struct sockaddr_storage addressStorage; 49 struct sockaddr_in *saddr = (struct sockaddr_in *) &addressStorage; 50 51 saddr->sin_port = htons (inPort); 52 saddr->sin_len = sizeof (struct sockaddr_in); 53 saddr->sin_family = AF_INET; 54 saddr->sin_addr.s_addr = INADDR_ANY; 55 56 err = bind (fd, (struct sockaddr *) saddr, saddr->sin_len); 57 if (err < 0) { err = errno; } 58 } 59 60 if (!err) { 61 err = listen (fd, 5); 62 if (err < 0) { err = errno; } 63 } 64 65 if (!err) { 66 printf ("listening on port %d\n", inPort); 67 *outFD = fd; 68 fd = -1; /* only close on error */ 69 } else { 70 printError (err, "SetupListeningSocket failed"); 71 } 72 73 if (fd >= 0) { close (fd); } 74 75 return err; 76} 77 78/* --------------------------------------------------------------------------- */ 79 80static int Authenticate (int inSocket, gss_ctx_id_t *outContext) 81{ 82 int err = 0; 83 OM_uint32 majorStatus; 84 OM_uint32 minorStatus = 0; 85 gss_ctx_id_t context = GSS_C_NO_CONTEXT; 86 87 char *inputTokenBuffer = NULL; 88 size_t inputTokenBufferLength = 0; 89 gss_buffer_desc inputToken; /* buffer received from the server */ 90 91 if (inSocket < 0) { err = EINVAL; } 92 if (outContext == NULL) { err = EINVAL; } 93 94 /* 95 * The main authentication loop: 96 * 97 * GSS is a multimechanism API. The number of packet exchanges required to authenticate 98 * varies between mechanisms. As a result, we need to loop reading "input tokens" from 99 * the client, calling gss_accept_sec_context on the "input tokens" and send the resulting 100 * "output tokens" back to the client until we get GSS_S_COMPLETE or an error. 101 * 102 * When we are done, save the client principal so we can make authorization checks. 103 */ 104 105 majorStatus = GSS_S_CONTINUE_NEEDED; 106 while (!err && (majorStatus != GSS_S_COMPLETE)) { 107 /* Clean up old input buffer */ 108 if (inputTokenBuffer != NULL) { 109 free (inputTokenBuffer); 110 inputTokenBuffer = NULL; /* don't double-free */ 111 } 112 113 err = ReadToken (inSocket, &inputTokenBuffer, &inputTokenBufferLength); 114 115 if (!err) { 116 /* Set up input buffers for the next run through the loop */ 117 inputToken.value = inputTokenBuffer; 118 inputToken.length = inputTokenBufferLength; 119 } 120 121 if (!err) { 122 gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */ 123 124 /* 125 * accept_sec_context does the actual work of taking the client's request and 126 * generating an appropriate reply. Note that we pass GSS_C_NO_CREDENTIAL for 127 * the service principal. This causes the server to accept any service principal 128 * in the server's keytab, which enables you to support multihomed hosts by having 129 * one key in the keytab for each host identity the server responds on. 130 * 131 * However, since we may have more keys in the keytab than we want the server 132 * to actually use, we will need to check which service principal the client used 133 * after authentication succeeds. See ServicePrincipalIsValidForService() for 134 * where you would put these checks. We don't check here since if we stopped 135 * responding in the middle of the authentication negotiation, the client 136 * would get an EOF, and the user wouldn't know what went wrong. 137 */ 138 139 printf ("Calling gss_accept_sec_context...\n"); 140 majorStatus = gss_accept_sec_context (&minorStatus, &context, GSS_C_NO_CREDENTIAL, 141 &inputToken, GSS_C_NO_CHANNEL_BINDINGS, NULL /* client_name */, 142 NULL /* mech_types */, &outputToken, NULL /* req_flags */, 143 NULL /* time_rec */, NULL /* delegated_cred_handle */); 144 145 if ((outputToken.length > 0) && (outputToken.value != NULL)) { 146 /* Send the output token to the client (even on error) */ 147 err = WriteToken (inSocket, outputToken.value, outputToken.length); 148 149 /* free the output token */ 150 gss_release_buffer (&minorStatus, &outputToken); 151 } 152 } 153 154 if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) { 155 printGSSErrors ("gss_accept_sec_context", majorStatus, minorStatus); 156 err = minorStatus ? minorStatus : majorStatus; 157 } 158 } 159 160 if (!err) { 161 *outContext = context; 162 } else { 163 printError (err, "Authenticate failed"); 164 } 165 166 return err; 167} 168 169/* --------------------------------------------------------------------------- */ 170 171static int ServicePrincipalIsValidForService (const char *inServicePrincipal) 172{ 173 int err = 0; 174 krb5_context context = NULL; 175 krb5_principal principal = NULL; 176 177 if (inServicePrincipal == NULL) { err = EINVAL; } 178 179 if (!err) { 180 err = krb5_init_context (&context); 181 } 182 183 if (!err) { 184 err = krb5_parse_name (context, inServicePrincipal, &principal); 185 } 186 187 if (!err) { 188 /* 189 * Here is where we check to see if the service principal the client used is valid. 190 * Typically we would just check that the first component is the service name. 191 * Here we check only if the server was started with the service name option. 192 */ 193 if ((gServiceName != NULL) && (strcmp (gServiceName, krb5_princ_name (context, principal)->data) != 0)) { 194 err = KRB5KRB_AP_WRONG_PRINC; 195 } 196 } 197 198 if (principal != NULL) { krb5_free_principal (context, principal); } 199 if (context != NULL) { krb5_free_context (context); } 200 201 return err; 202} 203 204 205/* --------------------------------------------------------------------------- */ 206 207static int ClientPrincipalIsAuthorizedForService (const char *inClientPrincipal) 208{ 209 int err = 0; 210 krb5_context context = NULL; 211 krb5_principal principal = NULL; 212 213 if (inClientPrincipal == NULL) { err = EINVAL; } 214 215 if (!err) { 216 err = krb5_init_context (&context); 217 } 218 219 if (!err) { 220 err = krb5_parse_name (context, inClientPrincipal, &principal); 221 } 222 223 if (!err) { 224 /* 225 * Here is where the server checks to see if the client principal should be allowed 226 * to use your service. Typically it should check both the name and the realm, 227 * since with cross-realm shared keys, a user at another realm may be trying to 228 * contact your service. Most sites don't want to let users from other realms 229 * use their services except for specific individuals. 230 */ 231 err = 0; 232 } 233 234 if (principal != NULL) { krb5_free_principal (context, principal); } 235 if (context != NULL) { krb5_free_context (context); } 236 237 return err; 238} 239 240/* --------------------------------------------------------------------------- */ 241 242static int Authorize (gss_ctx_id_t *inContext, int *outAuthorized, int *outAuthorizationError) 243{ 244 int err = 0; 245 OM_uint32 majorStatus; 246 OM_uint32 minorStatus = 0; 247 gss_name_t clientName = NULL; 248 gss_name_t serviceName = NULL; 249 char *clientPrincipal = NULL; 250 char *servicePrincipal = NULL; 251 252 if (outAuthorized == NULL) { err = EINVAL; } 253 if (outAuthorizationError == NULL) { err = EINVAL; } 254 255 if (!err) { 256 /* Get the client and service principals used to authenticate */ 257 majorStatus = gss_inquire_context (&minorStatus, *inContext, &clientName, &serviceName, 258 NULL, NULL, NULL, NULL, NULL); 259 if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } 260 } 261 262 if (!err) { 263 /* Pull the client principal string out of the gss name */ 264 gss_buffer_desc nameToken; 265 266 majorStatus = gss_display_name (&minorStatus, clientName, &nameToken, NULL); 267 if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } 268 269 if (!err) { 270 clientPrincipal = malloc (nameToken.length + 1); 271 if (clientPrincipal == NULL) { err = ENOMEM; } 272 } 273 274 if (!err) { 275 memcpy (clientPrincipal, nameToken.value, nameToken.length); 276 clientPrincipal[nameToken.length] = '\0'; 277 } 278 279 if (nameToken.value != NULL) { gss_release_buffer (&minorStatus, &nameToken); } 280 } 281 282 if (!err) { 283 /* Pull the service principal string out of the gss name */ 284 gss_buffer_desc nameToken; 285 286 majorStatus = gss_display_name (&minorStatus, serviceName, &nameToken, NULL); 287 if (majorStatus != GSS_S_COMPLETE) { err = minorStatus ? minorStatus : majorStatus; } 288 289 if (!err) { 290 servicePrincipal = malloc (nameToken.length + 1); 291 if (servicePrincipal == NULL) { err = ENOMEM; } 292 } 293 294 if (!err) { 295 memcpy (servicePrincipal, nameToken.value, nameToken.length); 296 servicePrincipal[nameToken.length] = '\0'; 297 } 298 299 if (nameToken.value != NULL) { gss_release_buffer (&minorStatus, &nameToken); } 300 } 301 302 if (!err) { 303 int authorizationErr = ServicePrincipalIsValidForService (servicePrincipal); 304 305 if (!authorizationErr) { 306 authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal); 307 } 308 309 printf ("'%s' is%s authorized for service '%s'\n", 310 clientPrincipal, authorizationErr ? " NOT" : "", servicePrincipal); 311 312 *outAuthorized = !authorizationErr; 313 *outAuthorizationError = authorizationErr; 314 } 315 316 if (clientPrincipal == NULL) { free (clientPrincipal); } 317 if (servicePrincipal == NULL) { free (servicePrincipal); } 318 319 return err; 320} 321 322/* --------------------------------------------------------------------------- */ 323 324static void Usage (const char *argv[]) 325{ 326 fprintf (stderr, "Usage: %s [--port portNumber] [--sname serviceName]\n", argv[0]); 327 exit (1); 328} 329 330/* --------------------------------------------------------------------------- */ 331 332int main (int argc, const char *argv[]) 333{ 334 int err = 0; 335 OM_uint32 minorStatus; 336 int port = kDefaultPort; 337 int listenFD = -1; 338 gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; 339 gss_buffer_desc outputToken = { 0, NULL }; 340 int i = 0; 341 342 for (i = 1; (i < argc) && !err; i++) { 343 if ((strcmp (argv[i], "--port") == 0) && (i < (argc - 1))) { 344 port = strtol (argv[++i], NULL, 0); 345 if (port == 0) { err = errno; } 346 } else if ((strcmp(argv[i], "--sname") == 0) && (i < (argc - 1))) { 347 gServiceName = argv[++i]; 348 } else { 349 err = EINVAL; 350 } 351 } 352 353 if (!err) { 354 printf ("%s: Starting up...\n", argv[0]); 355 356 err = SetupListeningSocket (port, &listenFD); 357 } 358 359 if (!err) { 360 int connectionErr = 0; 361 int connectionFD = -1; 362 int authorized = 0; 363 int authorizationError = 0; 364 365 connectionFD = accept (listenFD, NULL, NULL); 366 if (connectionFD < 0) { 367 if (errno != EINTR) { 368 err = errno; 369 } 370 //continue; /* Try again */ 371 } 372 373 printf ("Accepting new connection...\n"); 374 connectionErr = Authenticate (connectionFD, &gssContext); 375 376 if (!connectionErr) { 377 connectionErr = Authorize (&gssContext, &authorized, &authorizationError); 378 } 379 380 if (!connectionErr) { 381 char buffer[1024]; 382 memset (buffer, 0, sizeof (buffer)); 383 384 /* 385 * Here is where your protocol would go. This sample server just 386 * writes a nul terminated string to the client telling whether 387 * it was authorized. 388 */ 389 if (authorized) { 390 snprintf (buffer, sizeof (buffer), "SUCCESS!"); 391 } else { 392 snprintf (buffer, sizeof(buffer), "FAILURE! %s (err = %d)", 393 error_message (authorizationError), authorizationError); 394 } 395 connectionErr = WriteEncryptedToken (connectionFD, gssContext, buffer, strlen (buffer) + 1); 396 } 397 398 if (connectionErr) { 399 printError (connectionErr, "Connection failed"); 400 } 401 402 if (connectionFD >= 0) { printf ("Closing connection.\n"); close (connectionFD); } 403 } 404 405 if (err) { 406 if (err == EINVAL) { 407 Usage (argv); 408 } else { 409 printError (err, "Server failed"); 410 } 411 } 412 413 if (listenFD >= 0) { close (listenFD); } 414 if (gssContext != NULL) { gss_delete_sec_context (&minorStatus, &gssContext, &outputToken); } 415 if (outputToken.value != NULL) { gss_release_buffer (&minorStatus, &outputToken); } 416 417 return err ? -1 : 0; 418} 419 420