|
gsasl
1.7.6
|
00001 /* server.c --- Experimental SASL mechanism KERBEROS_V5, server side. 00002 * Copyright (C) 2003-2012 Simon Josefsson 00003 * 00004 * This file is part of GNU SASL Library. 00005 * 00006 * GNU SASL Library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public License 00008 * as published by the Free Software Foundation; either version 2.1 of 00009 * the License, or (at your option) any later version. 00010 * 00011 * GNU SASL Library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with GNU SASL Library; if not, write to the Free 00018 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 * Boston, MA 02110-1301, USA. 00020 * 00021 * NB! Shishi is licensed under GPL, so linking GSASL with it require 00022 * that you follow the GPL for GSASL as well. 00023 * 00024 */ 00025 00026 #include "kerberos_v5.h" 00027 00028 #include "shared.h" 00029 00030 struct _Gsasl_kerberos_v5_server_state 00031 { 00032 int firststep; 00033 Shishi *sh; 00034 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN]; 00035 char *random; 00036 int serverqops; 00037 uint32_t servermaxbuf; 00038 int clientqop; 00039 int clientmutual; 00040 uint32_t clientmaxbuf; 00041 char *username; 00042 char *userrealm; 00043 char *serverrealm; 00044 char *serverservice; 00045 char *serverhostname; 00046 char *password; 00047 Shishi_key *userkey; /* user's key derived with string2key */ 00048 Shishi_key *sessionkey; /* shared between client and server */ 00049 Shishi_key *sessiontktkey; /* known only by server */ 00050 Shishi_ap *ap; 00051 Shishi_as *as; 00052 Shishi_safe *safe; 00053 }; 00054 00055 int 00056 _gsasl_kerberos_v5_server_init (Gsasl_ctx * ctx) 00057 { 00058 if (!shishi_check_version (SHISHI_VERSION)) 00059 return GSASL_UNKNOWN_MECHANISM; 00060 00061 return GSASL_OK; 00062 } 00063 00064 int 00065 _gsasl_kerberos_v5_server_start (Gsasl_session * sctx, void **mech_data) 00066 { 00067 struct _Gsasl_kerberos_v5_server_state *state; 00068 int err; 00069 00070 state = malloc (sizeof (*state)); 00071 if (state == NULL) 00072 return GSASL_MALLOC_ERROR; 00073 memset (state, 0, sizeof (*state)); 00074 00075 state->random = (char *) malloc (RANDOM_LEN); 00076 if (state->random == NULL) 00077 return GSASL_MALLOC_ERROR; 00078 00079 err = shishi_init_server (&state->sh); 00080 if (err) 00081 return GSASL_KERBEROS_V5_INIT_ERROR; 00082 00083 err = shishi_randomize (state->sh, state->random, RANDOM_LEN); 00084 if (err) 00085 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00086 00087 /* This can be pretty much anything, the client will never have it. */ 00088 err = shishi_key_random (state->sh, SHISHI_AES256_CTS_HMAC_SHA1_96, 00089 &state->sessiontktkey); 00090 if (err) 00091 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00092 00093 err = shishi_as (state->sh, &state->as); 00094 if (err) 00095 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00096 00097 state->firststep = 1; 00098 state->serverqops = GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT; 00099 00100 *mech_data = state; 00101 00102 return GSASL_OK; 00103 } 00104 00105 int 00106 _gsasl_kerberos_v5_server_step (Gsasl_session * sctx, 00107 void *mech_data, 00108 const char *input, 00109 size_t input_len, 00110 char *output, size_t * output_len) 00111 { 00112 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 00113 Gsasl_server_callback_realm cb_realm; 00114 Gsasl_server_callback_qop cb_qop; 00115 Gsasl_server_callback_maxbuf cb_maxbuf; 00116 Gsasl_server_callback_cipher cb_cipher; 00117 Gsasl_server_callback_retrieve cb_retrieve; 00118 Gsasl_server_callback_service cb_service; 00119 unsigned char buf[BUFSIZ]; 00120 size_t buflen; 00121 Gsasl_ctx *ctx; 00122 ASN1_TYPE asn1; 00123 int err; 00124 00125 ctx = gsasl_server_ctx_get (sctx); 00126 if (ctx == NULL) 00127 return GSASL_CANNOT_GET_CTX; 00128 00129 cb_realm = gsasl_server_callback_realm_get (ctx); 00130 cb_qop = gsasl_server_callback_qop_get (ctx); 00131 cb_maxbuf = gsasl_server_callback_maxbuf_get (ctx); 00132 cb_retrieve = gsasl_server_callback_retrieve_get (ctx); 00133 cb_service = gsasl_server_callback_service_get (ctx); 00134 if (cb_service == NULL) 00135 return GSASL_NEED_SERVER_SERVICE_CALLBACK; 00136 00137 if (state->firststep) 00138 { 00139 uint32_t tmp; 00140 unsigned char *p; 00141 00142 /* 00143 * The initial server packet should contain one octet containing 00144 * a bit mask of supported security layers, four octets 00145 * indicating the maximum cipher-text buffer size the server is 00146 * able to receive (or 0 if no security layers are supported) in 00147 * network byte order, and then 16 octets containing random data 00148 * (see [4] on how random data might be generated). 00149 * 00150 * The security layers and their corresponding bit-masks are as 00151 * follows: 00152 * 00153 * Bit 0 No security layer 00154 * Bit 1 Integrity (KRB-SAFE) protection 00155 * Bit 2 Privacy (KRB-PRIV) protection 00156 * Bit 3 Mutual authentication is required (AP option MUTUAL- 00157 * REQUIRED must also be present). 00158 * 00159 * Other bit-masks may be defined in the future; bits which are 00160 * not understood must be negotiated off. 00161 * 00162 */ 00163 if (output && *output_len < BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN) 00164 return GSASL_TOO_SMALL_BUFFER; 00165 00166 p = &state->serverhello[0]; 00167 00168 if (cb_qop) 00169 state->serverqops = cb_qop (sctx); 00170 *p = 0; 00171 if (state->serverqops & GSASL_QOP_AUTH) 00172 *p |= GSASL_QOP_AUTH; 00173 if (state->serverqops & GSASL_QOP_AUTH_INT) 00174 *p |= GSASL_QOP_AUTH_INT; 00175 if (state->serverqops & GSASL_QOP_AUTH_CONF) 00176 *p |= GSASL_QOP_AUTH_CONF; 00177 /* XXX we always require mutual authentication for now */ 00178 *p |= MUTUAL; 00179 00180 if (!(state->serverqops & ~GSASL_QOP_AUTH)) 00181 state->servermaxbuf = 0; 00182 else if (cb_maxbuf) 00183 state->servermaxbuf = cb_maxbuf (sctx); 00184 else 00185 state->servermaxbuf = MAXBUF_DEFAULT; 00186 00187 tmp = htonl (state->servermaxbuf); 00188 memcpy (&state->serverhello[BITMAP_LEN], &tmp, MAXBUF_LEN); 00189 memcpy (&state->serverhello[BITMAP_LEN + MAXBUF_LEN], 00190 state->random, RANDOM_LEN); 00191 00192 if (output) 00193 memcpy (output, state->serverhello, SERVER_HELLO_LEN); 00194 *output_len = BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN; 00195 00196 state->firststep = 0; 00197 00198 return GSASL_NEEDS_MORE; 00199 } 00200 00201 if (cb_retrieve) 00202 { 00203 /* Non-infrastructure mode */ 00204 00205 if (*output_len < 2048) 00206 return GSASL_TOO_SMALL_BUFFER; 00207 00208 if (shishi_as_req_der_set (state->as, input, input_len) == SHISHI_OK) 00209 { 00210 Shishi_tkt *tkt; 00211 int etype, i; 00212 00213 tkt = shishi_as_tkt (state->as); 00214 if (!tkt) 00215 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00216 00217 i = 1; 00218 do 00219 { 00220 err = shishi_kdcreq_etype (state->sh, 00221 shishi_as_req (state->as), 00222 &etype, i); 00223 if (err == SHISHI_OK && shishi_cipher_supported_p (etype)) 00224 break; 00225 } 00226 while (err == SHISHI_OK); 00227 if (err != SHISHI_OK) 00228 return err; 00229 00230 /* XXX use a "preferred server kdc etype" from shishi instead? */ 00231 err = shishi_key_random (state->sh, etype, &state->sessionkey); 00232 if (err) 00233 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00234 00235 err = shishi_tkt_key_set (tkt, state->sessionkey); 00236 if (err) 00237 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00238 00239 buflen = sizeof (buf) - 1; 00240 err = shishi_kdcreq_cname_get (state->sh, 00241 shishi_as_req (state->as), 00242 buf, &buflen); 00243 if (err != SHISHI_OK) 00244 return err; 00245 buf[buflen] = '\0'; 00246 state->username = strdup (buf); 00247 00248 buflen = sizeof (buf) - 1; 00249 err = shishi_kdcreq_realm_get (state->sh, 00250 shishi_as_req (state->as), 00251 buf, &buflen); 00252 if (err != SHISHI_OK) 00253 return err; 00254 buf[buflen] = '\0'; 00255 state->userrealm = strdup (buf); 00256 00257 buflen = sizeof (buf) - 1; 00258 err = cb_retrieve (sctx, state->username, NULL, state->userrealm, 00259 NULL, &buflen); 00260 if (err != GSASL_OK) 00261 return err; 00262 00263 state->password = malloc (buflen + 1); 00264 if (state->password == NULL) 00265 return GSASL_MALLOC_ERROR; 00266 00267 err = cb_retrieve (sctx, state->username, NULL, state->userrealm, 00268 state->password, &buflen); 00269 if (err != GSASL_OK) 00270 return err; 00271 state->password[buflen] = '\0'; 00272 00273 buflen = sizeof (buf) - 1; 00274 if (cb_realm) 00275 { 00276 err = cb_realm (sctx, buf, &buflen, 0); 00277 if (err != GSASL_OK) 00278 return err; 00279 } 00280 else 00281 buflen = 0; 00282 buf[buflen] = '\0'; 00283 state->serverrealm = strdup (buf); 00284 00285 buflen = sizeof (buf) - 1; 00286 err = cb_service (sctx, buf, &buflen, NULL, NULL); 00287 if (err != GSASL_OK) 00288 return err; 00289 buf[buflen] = '\0'; 00290 state->serverservice = strdup (buf); 00291 00292 buflen = sizeof (buf) - 1; 00293 err = cb_service (sctx, NULL, NULL, buf, &buflen); 00294 if (err != GSASL_OK) 00295 return err; 00296 buf[buflen] = '\0'; 00297 state->serverhostname = strdup (buf); 00298 00299 /* XXX do some checking on realm and server name? Right now 00300 we simply doesn't care about what client requested and 00301 return a ticket for this server. This is bad. */ 00302 00303 err = shishi_tkt_clientrealm_set (tkt, state->userrealm, 00304 state->username); 00305 if (err) 00306 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00307 00308 { 00309 char *p; 00310 p = malloc (strlen (state->serverservice) + strlen ("/") + 00311 strlen (state->serverhostname) + 1); 00312 if (p == NULL) 00313 return GSASL_MALLOC_ERROR; 00314 sprintf (p, "%s/%s", state->serverservice, state->serverhostname); 00315 err = shishi_tkt_serverrealm_set (tkt, state->serverrealm, p); 00316 free (p); 00317 if (err) 00318 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00319 } 00320 00321 buflen = sizeof (buf); 00322 err = shishi_as_derive_salt (state->sh, 00323 shishi_as_req (state->as), 00324 shishi_as_rep (state->as), 00325 buf, &buflen); 00326 if (err != SHISHI_OK) 00327 return err; 00328 00329 err = shishi_key_from_string (state->sh, 00330 etype, 00331 state->password, 00332 strlen (state->password), 00333 buf, buflen, NULL, &state->userkey); 00334 if (err != SHISHI_OK) 00335 return err; 00336 00337 err = shishi_tkt_build (tkt, state->sessiontktkey); 00338 if (err) 00339 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00340 00341 err = shishi_as_rep_build (state->as, state->userkey); 00342 if (err) 00343 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00344 00345 #if DEBUG 00346 shishi_kdcreq_print (state->sh, stderr, shishi_as_req (state->as)); 00347 shishi_encticketpart_print (state->sh, stderr, 00348 shishi_tkt_encticketpart (tkt)); 00349 shishi_ticket_print (state->sh, stderr, shishi_tkt_ticket (tkt)); 00350 shishi_enckdcreppart_print (state->sh, stderr, 00351 shishi_tkt_enckdcreppart (state->as)); 00352 shishi_kdcrep_print (state->sh, stderr, shishi_as_rep (state->as)); 00353 #endif 00354 00355 err = shishi_as_rep_der (state->as, output, output_len); 00356 if (err) 00357 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00358 00359 return GSASL_NEEDS_MORE; 00360 } 00361 else if ((asn1 = shishi_der2asn1_apreq (state->sh, input, input_len))) 00362 { 00363 int adtype; 00364 00365 err = shishi_ap (state->sh, &state->ap); 00366 if (err) 00367 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00368 00369 shishi_ap_req_set (state->ap, asn1); 00370 00371 err = shishi_ap_req_process (state->ap, state->sessiontktkey); 00372 if (err) 00373 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00374 00375 #if DEBUG 00376 shishi_apreq_print (state->sh, stderr, shishi_ap_req (state->ap)); 00377 shishi_ticket_print (state->sh, stderr, 00378 shishi_tkt_ticket (shishi_ap_tkt (state->ap))); 00379 shishi_authenticator_print (state->sh, stderr, 00380 shishi_ap_authenticator (state->ap)); 00381 #endif 00382 00383 buflen = sizeof (buf); 00384 err = shishi_authenticator_authorizationdata 00385 (state->sh, shishi_ap_authenticator (state->ap), 00386 &adtype, buf, &buflen, 1); 00387 if (err) 00388 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00389 00390 if (adtype != 0xFF /* -1 in one-complements form */ || 00391 buflen < CLIENT_HELLO_LEN + SERVER_HELLO_LEN) 00392 return GSASL_AUTHENTICATION_ERROR; 00393 00394 { 00395 unsigned char clientbitmap; 00396 00397 memcpy (&clientbitmap, &buf[0], BITMAP_LEN); 00398 state->clientqop = 0; 00399 if (clientbitmap & GSASL_QOP_AUTH) 00400 state->clientqop |= GSASL_QOP_AUTH; 00401 if (clientbitmap & GSASL_QOP_AUTH_INT) 00402 state->clientqop |= GSASL_QOP_AUTH_INT; 00403 if (clientbitmap & GSASL_QOP_AUTH_CONF) 00404 state->clientqop |= GSASL_QOP_AUTH_CONF; 00405 if (clientbitmap & MUTUAL) 00406 state->clientmutual = 1; 00407 } 00408 memcpy (&state->clientmaxbuf, &input[BITMAP_LEN], MAXBUF_LEN); 00409 state->clientmaxbuf = ntohl (state->clientmaxbuf); 00410 00411 if (!(state->clientqop & state->serverqops)) 00412 return GSASL_AUTHENTICATION_ERROR; 00413 00414 /* XXX check clientmaxbuf too */ 00415 00416 if (memcmp (&buf[CLIENT_HELLO_LEN], 00417 state->serverhello, SERVER_HELLO_LEN) != 0) 00418 return GSASL_AUTHENTICATION_ERROR; 00419 00420 { 00421 char cksum[BUFSIZ]; 00422 int cksumlen; 00423 int cksumtype; 00424 Shishi_key *key; 00425 00426 key = shishi_tkt_key (shishi_as_tkt (state->as)); 00427 cksumtype = 00428 shishi_cipher_defaultcksumtype (shishi_key_type (key)); 00429 cksumlen = sizeof (cksum); 00430 err = shishi_checksum (state->sh, key, 00431 SHISHI_KEYUSAGE_APREQ_AUTHENTICATOR_CKSUM, 00432 cksumtype, buf, buflen, cksum, &cksumlen); 00433 if (err != SHISHI_OK) 00434 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00435 00436 buflen = sizeof (buf); 00437 err = shishi_authenticator_cksum 00438 (state->sh, 00439 shishi_ap_authenticator (state->ap), &cksumtype, buf, &buflen); 00440 if (err != SHISHI_OK) 00441 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00442 00443 if (buflen != cksumlen || memcmp (buf, cksum, buflen) != 0) 00444 return GSASL_AUTHENTICATION_ERROR; 00445 } 00446 00447 /* XXX use authorization_id */ 00448 00449 if (state->clientmutual) 00450 { 00451 err = shishi_ap_rep_build (state->ap); 00452 if (err) 00453 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00454 00455 err = shishi_ap_rep_der (state->ap, output, output_len); 00456 if (err) 00457 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00458 } 00459 else 00460 *output_len = 0; 00461 00462 return GSASL_OK; 00463 } 00464 } 00465 else 00466 { 00467 /* XXX Currently we only handle AS-REQ and AP-REQ in 00468 non-infrastructure mode. Supporting infrastructure mode is 00469 simple, just send the AS-REQ to the KDC and wait for AS-REP 00470 instead of creating AS-REP locally. 00471 00472 We should probably have a callback to decide policy: 00473 1) non-infrastructure mode (NIM) only 00474 2) infrastructure mode (IM) only 00475 3) proxied infrastructure mode (PIM) only 00476 4) NIM with fallback to IM (useful for local server overrides) 00477 5) IM with fallback to NIM (useful for admins if KDC is offline) 00478 6) ...etc with PIM too 00479 */ 00480 return GSASL_NEED_SERVER_RETRIEVE_CALLBACK; 00481 } 00482 00483 *output_len = 0; 00484 return GSASL_NEEDS_MORE; 00485 } 00486 00487 int 00488 _gsasl_kerberos_v5_server_encode (Gsasl_session * sctx, 00489 void *mech_data, 00490 const char *input, 00491 size_t input_len, 00492 char *output, size_t * output_len) 00493 { 00494 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 00495 int res; 00496 00497 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00498 { 00499 return GSASL_INTEGRITY_ERROR; 00500 } 00501 else if (state && state->sessionkey 00502 && state->clientqop & GSASL_QOP_AUTH_INT) 00503 { 00504 res = shishi_safe (state->sh, &state->safe); 00505 if (res != SHISHI_OK) 00506 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00507 00508 res = shishi_safe_set_user_data (state->sh, 00509 shishi_safe_safe (state->safe), 00510 input, input_len); 00511 if (res != SHISHI_OK) 00512 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00513 00514 res = shishi_safe_build (state->safe, state->sessionkey); 00515 if (res != SHISHI_OK) 00516 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00517 00518 res = shishi_safe_safe_der (state->safe, output, output_len); 00519 if (res != SHISHI_OK) 00520 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00521 } 00522 else 00523 { 00524 *output_len = input_len; 00525 *output = malloc (input_len); 00526 if (!*output) 00527 return GSASL_MALLOC_ERROR; 00528 memcpy (*output, input, input_len); 00529 } 00530 00531 return GSASL_OK; 00532 } 00533 00534 int 00535 _gsasl_kerberos_v5_server_decode (Gsasl_session * sctx, 00536 void *mech_data, 00537 const char *input, 00538 size_t input_len, 00539 char *output, size_t * output_len) 00540 { 00541 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 00542 int res; 00543 00544 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00545 { 00546 return GSASL_INTEGRITY_ERROR; 00547 } 00548 else if (state && state->sessionkey 00549 && state->clientqop & GSASL_QOP_AUTH_INT) 00550 { 00551 Shishi_asn1 asn1safe; 00552 00553 res = shishi_safe (state->sh, &state->safe); 00554 if (res != SHISHI_OK) 00555 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00556 00557 res = shishi_safe_safe_der_set (state->safe, input, input_len); 00558 if (res != SHISHI_OK) 00559 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00560 00561 res = shishi_safe_verify (state->safe, state->sessionkey); 00562 if (res != SHISHI_OK) 00563 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00564 00565 res = shishi_safe_user_data (state->sh, shishi_safe_safe (state->safe), 00566 output, output_len); 00567 if (res != SHISHI_OK) 00568 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00569 00570 return GSASL_OK; 00571 } 00572 else 00573 { 00574 *output_len = input_len; 00575 *output = malloc (input_len); 00576 if (!*output) 00577 return GSASL_MALLOC_ERROR; 00578 memcpy (*output, input, input_len); 00579 } 00580 00581 00582 return GSASL_OK; 00583 } 00584 00585 int 00586 _gsasl_kerberos_v5_server_finish (Gsasl_session * sctx, void *mech_data) 00587 { 00588 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 00589 00590 shishi_done (state->sh); 00591 00592 free (state->username); 00593 free (state->password); 00594 free (state->random); 00595 free (state); 00596 00597 return GSASL_OK; 00598 }
1.7.6.1