|
gsasl
1.8.0
|
00001 /* kerberos_v5.c --- Implementation of experimental SASL mechanism KERBEROS_V5. 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 /* Get Shishi API. */ 00029 #include <shishi.h> 00030 00031 #ifdef HAVE_NETINET_IN_H 00032 #include <netinet/in.h> /* ntohl */ 00033 #endif 00034 00035 #define DEBUG 0 00036 00037 #define BITMAP_LEN 1 00038 #define MAXBUF_LEN 4 00039 #define RANDOM_LEN 16 00040 #define MUTUAL (1 << 3) 00041 00042 #define SERVER_HELLO_LEN BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN 00043 #define CLIENT_HELLO_LEN BITMAP_LEN + MAXBUF_LEN 00044 00045 #define MAXBUF_DEFAULT 65536 00046 00047 /* Client */ 00048 00049 #ifdef USE_CLIENT 00050 00051 struct _Gsasl_kerberos_v5_client_state 00052 { 00053 int step; 00054 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN]; 00055 int serverqops; 00056 int clientqop; 00057 int servermutual; 00058 uint32_t servermaxbuf; 00059 uint32_t clientmaxbuf; 00060 Shishi *sh; 00061 Shishi_tkt *tkt; 00062 Shishi_as *as; 00063 Shishi_ap *ap; 00064 Shishi_key *sessionkey; 00065 Shishi_safe *safe; 00066 }; 00067 00068 int 00069 _gsasl_kerberos_v5_client_init (Gsasl_ctx * ctx) 00070 { 00071 if (!shishi_check_version (SHISHI_VERSION)) 00072 return GSASL_UNKNOWN_MECHANISM; 00073 00074 return GSASL_OK; 00075 } 00076 00077 int 00078 _gsasl_kerberos_v5_client_start (Gsasl_session * sctx, void **mech_data) 00079 { 00080 struct _Gsasl_kerberos_v5_client_state *state; 00081 Gsasl_ctx *ctx; 00082 int err; 00083 00084 state = malloc (sizeof (*state)); 00085 if (state == NULL) 00086 return GSASL_MALLOC_ERROR; 00087 00088 memset (state, 0, sizeof (*state)); 00089 00090 err = shishi_init (&state->sh); 00091 if (err) 00092 return GSASL_KERBEROS_V5_INIT_ERROR; 00093 00094 state->step = 0; 00095 state->clientqop = GSASL_QOP_AUTH_INT; 00096 00097 *mech_data = state; 00098 00099 return GSASL_OK; 00100 } 00101 00102 #define STEP_FIRST 0 00103 #define STEP_NONINFRA_SEND_ASREQ 1 00104 #define STEP_NONINFRA_WAIT_ASREP 2 00105 #define STEP_NONINFRA_SEND_APREQ 3 00106 #define STEP_NONINFRA_WAIT_APREP 4 00107 #define STEP_SUCCESS 5 00108 00109 int 00110 _gsasl_kerberos_v5_client_step (Gsasl_session * sctx, 00111 void *mech_data, 00112 const char *input, 00113 size_t input_len, 00114 char *output, size_t * output_len) 00115 { 00116 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00117 Gsasl_client_callback_authentication_id cb_authentication_id; 00118 Gsasl_client_callback_authorization_id cb_authorization_id; 00119 Gsasl_client_callback_qop cb_qop; 00120 Gsasl_client_callback_realm cb_realm; 00121 Gsasl_client_callback_password cb_password; 00122 Gsasl_client_callback_service cb_service; 00123 Gsasl_client_callback_maxbuf cb_maxbuf; 00124 Gsasl_ctx *ctx; 00125 int res; 00126 int len; 00127 00128 ctx = gsasl_client_ctx_get (sctx); 00129 if (ctx == NULL) 00130 return GSASL_CANNOT_GET_CTX; 00131 00132 /* These are optional */ 00133 cb_realm = gsasl_client_callback_realm_get (ctx); 00134 cb_service = gsasl_client_callback_service_get (ctx); 00135 cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx); 00136 cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx); 00137 cb_qop = gsasl_client_callback_qop_get (ctx); 00138 cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx); 00139 00140 /* Only optionally needed in infrastructure mode */ 00141 cb_password = gsasl_client_callback_password_get (ctx); 00142 if (cb_password == NULL) 00143 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK; 00144 00145 /* I think we really need this one */ 00146 cb_service = gsasl_client_callback_service_get (ctx); 00147 if (cb_service == NULL) 00148 return GSASL_NEED_CLIENT_SERVICE_CALLBACK; 00149 00150 switch (state->step) 00151 { 00152 case STEP_FIRST: 00153 if (input == NULL) 00154 { 00155 *output_len = 0; 00156 return GSASL_NEEDS_MORE; 00157 } 00158 00159 if (input_len != SERVER_HELLO_LEN) 00160 return GSASL_MECHANISM_PARSE_ERROR; 00161 00162 memcpy (state->serverhello, input, input_len); 00163 00164 { 00165 unsigned char serverbitmap; 00166 00167 memcpy (&serverbitmap, input, BITMAP_LEN); 00168 state->serverqops = 0; 00169 if (serverbitmap & GSASL_QOP_AUTH) 00170 state->serverqops |= GSASL_QOP_AUTH; 00171 if (serverbitmap & GSASL_QOP_AUTH_INT) 00172 state->serverqops |= GSASL_QOP_AUTH_INT; 00173 if (serverbitmap & GSASL_QOP_AUTH_CONF) 00174 state->serverqops |= GSASL_QOP_AUTH_CONF; 00175 if (serverbitmap & MUTUAL) 00176 state->servermutual = 1; 00177 } 00178 memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN); 00179 state->servermaxbuf = ntohl (state->servermaxbuf); 00180 00181 if (cb_qop) 00182 state->clientqop = cb_qop (sctx, state->serverqops); 00183 00184 if (!(state->serverqops & state->clientqop & 00185 (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF))) 00186 return GSASL_AUTHENTICATION_ERROR; 00187 00188 /* XXX for now we require server authentication */ 00189 if (!state->servermutual) 00190 return GSASL_AUTHENTICATION_ERROR; 00191 00192 /* Decide policy here: non-infrastructure, infrastructure or proxy. 00193 * 00194 * A callback to decide should be added, but without the default 00195 * should be: 00196 * 00197 * IF shishi_tktset_get_for_server() THEN 00198 * INFRASTRUCTURE MODE 00199 * ELSE IF shishi_realm_for_server(server) THEN 00200 * PROXY INFRASTRUCTURE (then fallback to NIM?) 00201 * ELSE 00202 * NON-INFRASTRUCTURE MODE 00203 */ 00204 state->step = STEP_NONINFRA_SEND_APREQ; /* only NIM for now.. */ 00205 /* fall through */ 00206 00207 case STEP_NONINFRA_SEND_ASREQ: 00208 res = shishi_as (state->sh, &state->as); 00209 if (res) 00210 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00211 00212 if (cb_authentication_id) /* Shishi defaults to one otherwise */ 00213 { 00214 len = *output_len - 1; 00215 res = cb_authentication_id (sctx, output, &len); 00216 if (res != GSASL_OK) 00217 return res; 00218 output[len] = '\0'; 00219 00220 res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as), 00221 SHISHI_NT_UNKNOWN, output); 00222 if (res != GSASL_OK) 00223 return res; 00224 } 00225 00226 if (cb_realm) 00227 { 00228 len = *output_len - 1; 00229 res = cb_realm (sctx, output, &len); 00230 if (res != GSASL_OK) 00231 return res; 00232 } 00233 else 00234 len = 0; 00235 00236 output[len] = '\0'; 00237 res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as), 00238 output); 00239 if (res != GSASL_OK) 00240 return res; 00241 00242 if (cb_service) 00243 { 00244 char *sname[3]; 00245 size_t servicelen = 0; 00246 size_t hostnamelen = 0; 00247 00248 res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen, 00249 /* XXX support servicename a'la DIGEST-MD5 too? */ 00250 NULL, NULL); 00251 if (res != GSASL_OK) 00252 return res; 00253 00254 if (*output_len < servicelen + 1 + hostnamelen + 1) 00255 return GSASL_TOO_SMALL_BUFFER; 00256 00257 sname[0] = &output[0]; 00258 sname[1] = &output[servicelen + 2]; 00259 sname[2] = NULL; 00260 00261 res = cb_service (sctx, sname[0], &servicelen, 00262 sname[1], &hostnamelen, NULL, NULL); 00263 if (res != GSASL_OK) 00264 return res; 00265 00266 sname[0][servicelen] = '\0'; 00267 sname[1][hostnamelen] = '\0'; 00268 00269 res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as), 00270 SHISHI_NT_UNKNOWN, sname); 00271 if (res != GSASL_OK) 00272 return res; 00273 } 00274 00275 /* XXX query application for encryption types and set the etype 00276 field? Already configured by shishi though... */ 00277 00278 res = shishi_a2d (state->sh, shishi_as_req (state->as), 00279 output, output_len); 00280 if (res != SHISHI_OK) 00281 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00282 00283 state->step = STEP_NONINFRA_WAIT_ASREP; 00284 00285 res = GSASL_NEEDS_MORE; 00286 break; 00287 00288 case STEP_NONINFRA_WAIT_ASREP: 00289 if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK) 00290 return GSASL_MECHANISM_PARSE_ERROR; 00291 00292 /* XXX? password stored in callee's output buffer */ 00293 len = *output_len - 1; 00294 res = cb_password (sctx, output, &len); 00295 if (res != GSASL_OK && res != GSASL_NEEDS_MORE) 00296 return res; 00297 output[len] = '\0'; 00298 00299 res = shishi_as_rep_process (state->as, NULL, output); 00300 if (res != SHISHI_OK) 00301 return GSASL_AUTHENTICATION_ERROR; 00302 00303 state->step = STEP_NONINFRA_SEND_APREQ; 00304 /* fall through */ 00305 00306 case STEP_NONINFRA_SEND_APREQ: 00307 if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN) 00308 return GSASL_TOO_SMALL_BUFFER; 00309 00310 if (!(state->clientqop & ~GSASL_QOP_AUTH)) 00311 state->clientmaxbuf = 0; 00312 else if (cb_maxbuf) 00313 state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf); 00314 else 00315 state->clientmaxbuf = MAXBUF_DEFAULT; 00316 00317 /* XXX for now we require server authentication */ 00318 output[0] = state->clientqop | MUTUAL; 00319 { 00320 uint32_t tmp; 00321 00322 tmp = ntohl (state->clientmaxbuf); 00323 memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN); 00324 } 00325 memcpy (&output[CLIENT_HELLO_LEN], state->serverhello, 00326 SERVER_HELLO_LEN); 00327 00328 if (cb_authorization_id) 00329 { 00330 len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN; 00331 res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN + 00332 SERVER_HELLO_LEN], &len); 00333 } 00334 else 00335 len = 0; 00336 00337 len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN; 00338 res = shishi_ap_tktoptionsdata (state->sh, 00339 &state->ap, 00340 shishi_as_tkt (state->as), 00341 SHISHI_APOPTIONS_MUTUAL_REQUIRED, 00342 output, len); 00343 if (res != SHISHI_OK) 00344 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00345 00346 res = shishi_authenticator_add_authorizationdata 00347 (state->sh, shishi_ap_authenticator (state->ap), -1, output, len); 00348 if (res != SHISHI_OK) 00349 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00350 00351 /* XXX set realm in AP-REQ and Authenticator */ 00352 00353 res = shishi_ap_req_der (state->ap, output, output_len); 00354 if (res != SHISHI_OK) 00355 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00356 00357 state->step = STEP_NONINFRA_WAIT_APREP; 00358 00359 res = GSASL_NEEDS_MORE; 00360 break; 00361 00362 case STEP_NONINFRA_WAIT_APREP: 00363 if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK) 00364 return GSASL_MECHANISM_PARSE_ERROR; 00365 00366 res = shishi_ap_rep_verify (state->ap); 00367 if (res != SHISHI_OK) 00368 return GSASL_AUTHENTICATION_ERROR; 00369 00370 state->step = STEP_SUCCESS; 00371 00372 /* XXX support AP session keys */ 00373 state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as)); 00374 00375 *output_len = 0; 00376 res = GSASL_OK; 00377 break; 00378 00379 default: 00380 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; 00381 break; 00382 } 00383 00384 return res; 00385 } 00386 00387 int 00388 _gsasl_kerberos_v5_client_encode (Gsasl_session * sctx, 00389 void *mech_data, 00390 const char *input, 00391 size_t input_len, 00392 char *output, size_t * output_len) 00393 { 00394 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00395 int res; 00396 00397 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00398 { 00399 /* XXX */ 00400 } 00401 else if (state && state->sessionkey 00402 && state->clientqop & GSASL_QOP_AUTH_INT) 00403 { 00404 res = shishi_safe (state->sh, &state->safe); 00405 if (res != SHISHI_OK) 00406 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00407 00408 res = shishi_safe_set_user_data (state->sh, 00409 shishi_safe_safe (state->safe), 00410 input, input_len); 00411 if (res != SHISHI_OK) 00412 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00413 00414 res = shishi_safe_build (state->safe, state->sessionkey); 00415 if (res != SHISHI_OK) 00416 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00417 00418 res = shishi_safe_safe_der (state->safe, output, output_len); 00419 if (res != SHISHI_OK) 00420 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00421 } 00422 else 00423 { 00424 *output_len = input_len; 00425 if (output) 00426 memcpy (output, input, input_len); 00427 return GSASL_OK; 00428 } 00429 00430 return GSASL_OK; 00431 } 00432 00433 int 00434 _gsasl_kerberos_v5_client_decode (Gsasl_session * sctx, 00435 void *mech_data, 00436 const char *input, 00437 size_t input_len, 00438 char *output, size_t * output_len) 00439 { 00440 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00441 00442 puts ("cdecode"); 00443 00444 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00445 { 00446 /* XXX */ 00447 } 00448 else if (state && state->sessionkey 00449 && state->clientqop & GSASL_QOP_AUTH_INT) 00450 { 00451 puts ("decode"); 00452 } 00453 else 00454 { 00455 *output_len = input_len; 00456 if (output) 00457 memcpy (output, input, input_len); 00458 return GSASL_OK; 00459 } 00460 00461 return GSASL_OK; 00462 } 00463 00464 int 00465 _gsasl_kerberos_v5_client_finish (Gsasl_session * sctx, void *mech_data) 00466 { 00467 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00468 00469 shishi_done (state->sh); 00470 free (state); 00471 00472 return GSASL_OK; 00473 } 00474 00475 #endif /* USE_CLIENT */ 00476 00477 /* Server */ 00478 00479 #ifdef USE_SERVER 00480 00481 struct _Gsasl_kerberos_v5_server_state 00482 { 00483 int firststep; 00484 Shishi *sh; 00485 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN]; 00486 char *random; 00487 int serverqops; 00488 uint32_t servermaxbuf; 00489 int clientqop; 00490 int clientmutual; 00491 uint32_t clientmaxbuf; 00492 char *username; 00493 char *userrealm; 00494 char *serverrealm; 00495 char *serverservice; 00496 char *serverhostname; 00497 char *password; 00498 Shishi_key *userkey; /* user's key derived with string2key */ 00499 Shishi_key *sessionkey; /* shared between client and server */ 00500 Shishi_key *sessiontktkey; /* known only by server */ 00501 Shishi_ap *ap; 00502 Shishi_as *as; 00503 Shishi_safe *safe; 00504 }; 00505 00506 int 00507 _gsasl_kerberos_v5_server_init (Gsasl_ctx * ctx) 00508 { 00509 if (!shishi_check_version (SHISHI_VERSION)) 00510 return GSASL_UNKNOWN_MECHANISM; 00511 00512 return GSASL_OK; 00513 } 00514 00515 int 00516 _gsasl_kerberos_v5_server_start (Gsasl_session * sctx, void **mech_data) 00517 { 00518 struct _Gsasl_kerberos_v5_server_state *state; 00519 int err; 00520 00521 state = malloc (sizeof (*state)); 00522 if (state == NULL) 00523 return GSASL_MALLOC_ERROR; 00524 memset (state, 0, sizeof (*state)); 00525 00526 state->random = (char *) malloc (RANDOM_LEN); 00527 if (state->random == NULL) 00528 return GSASL_MALLOC_ERROR; 00529 00530 err = shishi_init_server (&state->sh); 00531 if (err) 00532 return GSASL_KERBEROS_V5_INIT_ERROR; 00533 00534 err = shishi_randomize (state->sh, state->random, RANDOM_LEN); 00535 if (err) 00536 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00537 00538 /* This can be pretty much anything, the client will never have it. */ 00539 err = shishi_key_random (state->sh, SHISHI_AES256_CTS_HMAC_SHA1_96, 00540 &state->sessiontktkey); 00541 if (err) 00542 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00543 00544 err = shishi_as (state->sh, &state->as); 00545 if (err) 00546 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00547 00548 state->firststep = 1; 00549 state->serverqops = GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT; 00550 00551 *mech_data = state; 00552 00553 return GSASL_OK; 00554 } 00555 00556 int 00557 _gsasl_kerberos_v5_server_step (Gsasl_session * sctx, 00558 void *mech_data, 00559 const char *input, 00560 size_t input_len, 00561 char *output, size_t * output_len) 00562 { 00563 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 00564 Gsasl_server_callback_realm cb_realm; 00565 Gsasl_server_callback_qop cb_qop; 00566 Gsasl_server_callback_maxbuf cb_maxbuf; 00567 Gsasl_server_callback_cipher cb_cipher; 00568 Gsasl_server_callback_retrieve cb_retrieve; 00569 Gsasl_server_callback_service cb_service; 00570 unsigned char buf[BUFSIZ]; 00571 size_t buflen; 00572 Gsasl_ctx *ctx; 00573 ASN1_TYPE asn1; 00574 int err; 00575 00576 ctx = gsasl_server_ctx_get (sctx); 00577 if (ctx == NULL) 00578 return GSASL_CANNOT_GET_CTX; 00579 00580 cb_realm = gsasl_server_callback_realm_get (ctx); 00581 cb_qop = gsasl_server_callback_qop_get (ctx); 00582 cb_maxbuf = gsasl_server_callback_maxbuf_get (ctx); 00583 cb_retrieve = gsasl_server_callback_retrieve_get (ctx); 00584 cb_service = gsasl_server_callback_service_get (ctx); 00585 if (cb_service == NULL) 00586 return GSASL_NEED_SERVER_SERVICE_CALLBACK; 00587 00588 if (state->firststep) 00589 { 00590 uint32_t tmp; 00591 unsigned char *p; 00592 00593 /* 00594 * The initial server packet should contain one octet containing 00595 * a bit mask of supported security layers, four octets 00596 * indicating the maximum cipher-text buffer size the server is 00597 * able to receive (or 0 if no security layers are supported) in 00598 * network byte order, and then 16 octets containing random data 00599 * (see [4] on how random data might be generated). 00600 * 00601 * The security layers and their corresponding bit-masks are as 00602 * follows: 00603 * 00604 * Bit 0 No security layer 00605 * Bit 1 Integrity (KRB-SAFE) protection 00606 * Bit 2 Privacy (KRB-PRIV) protection 00607 * Bit 3 Mutual authentication is required (AP option MUTUAL- 00608 * REQUIRED must also be present). 00609 * 00610 * Other bit-masks may be defined in the future; bits which are 00611 * not understood must be negotiated off. 00612 * 00613 */ 00614 if (output && *output_len < BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN) 00615 return GSASL_TOO_SMALL_BUFFER; 00616 00617 p = &state->serverhello[0]; 00618 00619 if (cb_qop) 00620 state->serverqops = cb_qop (sctx); 00621 *p = 0; 00622 if (state->serverqops & GSASL_QOP_AUTH) 00623 *p |= GSASL_QOP_AUTH; 00624 if (state->serverqops & GSASL_QOP_AUTH_INT) 00625 *p |= GSASL_QOP_AUTH_INT; 00626 if (state->serverqops & GSASL_QOP_AUTH_CONF) 00627 *p |= GSASL_QOP_AUTH_CONF; 00628 /* XXX we always require mutual authentication for now */ 00629 *p |= MUTUAL; 00630 00631 if (!(state->serverqops & ~GSASL_QOP_AUTH)) 00632 state->servermaxbuf = 0; 00633 else if (cb_maxbuf) 00634 state->servermaxbuf = cb_maxbuf (sctx); 00635 else 00636 state->servermaxbuf = MAXBUF_DEFAULT; 00637 00638 tmp = htonl (state->servermaxbuf); 00639 memcpy (&state->serverhello[BITMAP_LEN], &tmp, MAXBUF_LEN); 00640 memcpy (&state->serverhello[BITMAP_LEN + MAXBUF_LEN], 00641 state->random, RANDOM_LEN); 00642 00643 if (output) 00644 memcpy (output, state->serverhello, SERVER_HELLO_LEN); 00645 *output_len = BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN; 00646 00647 state->firststep = 0; 00648 00649 return GSASL_NEEDS_MORE; 00650 } 00651 00652 if (cb_retrieve) 00653 { 00654 /* Non-infrastructure mode */ 00655 00656 if (*output_len < 2048) 00657 return GSASL_TOO_SMALL_BUFFER; 00658 00659 if (shishi_as_req_der_set (state->as, input, input_len) == SHISHI_OK) 00660 { 00661 Shishi_tkt *tkt; 00662 int etype, i; 00663 00664 tkt = shishi_as_tkt (state->as); 00665 if (!tkt) 00666 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00667 00668 i = 1; 00669 do 00670 { 00671 err = shishi_kdcreq_etype (state->sh, 00672 shishi_as_req (state->as), 00673 &etype, i); 00674 if (err == SHISHI_OK && shishi_cipher_supported_p (etype)) 00675 break; 00676 } 00677 while (err == SHISHI_OK); 00678 if (err != SHISHI_OK) 00679 return err; 00680 00681 /* XXX use a "preferred server kdc etype" from shishi instead? */ 00682 err = shishi_key_random (state->sh, etype, &state->sessionkey); 00683 if (err) 00684 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00685 00686 err = shishi_tkt_key_set (tkt, state->sessionkey); 00687 if (err) 00688 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00689 00690 buflen = sizeof (buf) - 1; 00691 err = shishi_kdcreq_cname_get (state->sh, 00692 shishi_as_req (state->as), 00693 buf, &buflen); 00694 if (err != SHISHI_OK) 00695 return err; 00696 buf[buflen] = '\0'; 00697 state->username = strdup (buf); 00698 00699 buflen = sizeof (buf) - 1; 00700 err = shishi_kdcreq_realm_get (state->sh, 00701 shishi_as_req (state->as), 00702 buf, &buflen); 00703 if (err != SHISHI_OK) 00704 return err; 00705 buf[buflen] = '\0'; 00706 state->userrealm = strdup (buf); 00707 00708 buflen = sizeof (buf) - 1; 00709 err = cb_retrieve (sctx, state->username, NULL, state->userrealm, 00710 NULL, &buflen); 00711 if (err != GSASL_OK) 00712 return err; 00713 00714 state->password = malloc (buflen + 1); 00715 if (state->password == NULL) 00716 return GSASL_MALLOC_ERROR; 00717 00718 err = cb_retrieve (sctx, state->username, NULL, state->userrealm, 00719 state->password, &buflen); 00720 if (err != GSASL_OK) 00721 return err; 00722 state->password[buflen] = '\0'; 00723 00724 buflen = sizeof (buf) - 1; 00725 if (cb_realm) 00726 { 00727 err = cb_realm (sctx, buf, &buflen, 0); 00728 if (err != GSASL_OK) 00729 return err; 00730 } 00731 else 00732 buflen = 0; 00733 buf[buflen] = '\0'; 00734 state->serverrealm = strdup (buf); 00735 00736 buflen = sizeof (buf) - 1; 00737 err = cb_service (sctx, buf, &buflen, NULL, NULL); 00738 if (err != GSASL_OK) 00739 return err; 00740 buf[buflen] = '\0'; 00741 state->serverservice = strdup (buf); 00742 00743 buflen = sizeof (buf) - 1; 00744 err = cb_service (sctx, NULL, NULL, buf, &buflen); 00745 if (err != GSASL_OK) 00746 return err; 00747 buf[buflen] = '\0'; 00748 state->serverhostname = strdup (buf); 00749 00750 /* XXX do some checking on realm and server name? Right now 00751 we simply doesn't care about what client requested and 00752 return a ticket for this server. This is bad. */ 00753 00754 err = shishi_tkt_clientrealm_set (tkt, state->userrealm, 00755 state->username); 00756 if (err) 00757 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00758 00759 { 00760 char *p; 00761 p = malloc (strlen (state->serverservice) + strlen ("/") + 00762 strlen (state->serverhostname) + 1); 00763 if (p == NULL) 00764 return GSASL_MALLOC_ERROR; 00765 sprintf (p, "%s/%s", state->serverservice, state->serverhostname); 00766 err = shishi_tkt_serverrealm_set (tkt, state->serverrealm, p); 00767 free (p); 00768 if (err) 00769 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00770 } 00771 00772 buflen = sizeof (buf); 00773 err = shishi_as_derive_salt (state->sh, 00774 shishi_as_req (state->as), 00775 shishi_as_rep (state->as), 00776 buf, &buflen); 00777 if (err != SHISHI_OK) 00778 return err; 00779 00780 err = shishi_key_from_string (state->sh, 00781 etype, 00782 state->password, 00783 strlen (state->password), 00784 buf, buflen, NULL, &state->userkey); 00785 if (err != SHISHI_OK) 00786 return err; 00787 00788 err = shishi_tkt_build (tkt, state->sessiontktkey); 00789 if (err) 00790 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00791 00792 err = shishi_as_rep_build (state->as, state->userkey); 00793 if (err) 00794 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00795 00796 #if DEBUG 00797 shishi_kdcreq_print (state->sh, stderr, shishi_as_req (state->as)); 00798 shishi_encticketpart_print (state->sh, stderr, 00799 shishi_tkt_encticketpart (tkt)); 00800 shishi_ticket_print (state->sh, stderr, shishi_tkt_ticket (tkt)); 00801 shishi_enckdcreppart_print (state->sh, stderr, 00802 shishi_tkt_enckdcreppart (state->as)); 00803 shishi_kdcrep_print (state->sh, stderr, shishi_as_rep (state->as)); 00804 #endif 00805 00806 err = shishi_as_rep_der (state->as, output, output_len); 00807 if (err) 00808 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00809 00810 return GSASL_NEEDS_MORE; 00811 } 00812 else if ((asn1 = shishi_der2asn1_apreq (state->sh, input, input_len))) 00813 { 00814 int adtype; 00815 00816 err = shishi_ap (state->sh, &state->ap); 00817 if (err) 00818 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00819 00820 shishi_ap_req_set (state->ap, asn1); 00821 00822 err = shishi_ap_req_process (state->ap, state->sessiontktkey); 00823 if (err) 00824 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00825 00826 #if DEBUG 00827 shishi_apreq_print (state->sh, stderr, shishi_ap_req (state->ap)); 00828 shishi_ticket_print (state->sh, stderr, 00829 shishi_tkt_ticket (shishi_ap_tkt (state->ap))); 00830 shishi_authenticator_print (state->sh, stderr, 00831 shishi_ap_authenticator (state->ap)); 00832 #endif 00833 00834 buflen = sizeof (buf); 00835 err = shishi_authenticator_authorizationdata 00836 (state->sh, shishi_ap_authenticator (state->ap), 00837 &adtype, buf, &buflen, 1); 00838 if (err) 00839 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00840 00841 if (adtype != 0xFF /* -1 in one-complements form */ || 00842 buflen < CLIENT_HELLO_LEN + SERVER_HELLO_LEN) 00843 return GSASL_AUTHENTICATION_ERROR; 00844 00845 { 00846 unsigned char clientbitmap; 00847 00848 memcpy (&clientbitmap, &buf[0], BITMAP_LEN); 00849 state->clientqop = 0; 00850 if (clientbitmap & GSASL_QOP_AUTH) 00851 state->clientqop |= GSASL_QOP_AUTH; 00852 if (clientbitmap & GSASL_QOP_AUTH_INT) 00853 state->clientqop |= GSASL_QOP_AUTH_INT; 00854 if (clientbitmap & GSASL_QOP_AUTH_CONF) 00855 state->clientqop |= GSASL_QOP_AUTH_CONF; 00856 if (clientbitmap & MUTUAL) 00857 state->clientmutual = 1; 00858 } 00859 memcpy (&state->clientmaxbuf, &input[BITMAP_LEN], MAXBUF_LEN); 00860 state->clientmaxbuf = ntohl (state->clientmaxbuf); 00861 00862 if (!(state->clientqop & state->serverqops)) 00863 return GSASL_AUTHENTICATION_ERROR; 00864 00865 /* XXX check clientmaxbuf too */ 00866 00867 if (memcmp (&buf[CLIENT_HELLO_LEN], 00868 state->serverhello, SERVER_HELLO_LEN) != 0) 00869 return GSASL_AUTHENTICATION_ERROR; 00870 00871 { 00872 char cksum[BUFSIZ]; 00873 int cksumlen; 00874 int cksumtype; 00875 Shishi_key *key; 00876 00877 key = shishi_tkt_key (shishi_as_tkt (state->as)); 00878 cksumtype = 00879 shishi_cipher_defaultcksumtype (shishi_key_type (key)); 00880 cksumlen = sizeof (cksum); 00881 err = shishi_checksum (state->sh, key, 00882 SHISHI_KEYUSAGE_APREQ_AUTHENTICATOR_CKSUM, 00883 cksumtype, buf, buflen, cksum, &cksumlen); 00884 if (err != SHISHI_OK) 00885 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00886 00887 buflen = sizeof (buf); 00888 err = shishi_authenticator_cksum 00889 (state->sh, 00890 shishi_ap_authenticator (state->ap), &cksumtype, buf, &buflen); 00891 if (err != SHISHI_OK) 00892 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00893 00894 if (buflen != cksumlen || memcmp (buf, cksum, buflen) != 0) 00895 return GSASL_AUTHENTICATION_ERROR; 00896 } 00897 00898 /* XXX use authorization_id */ 00899 00900 if (state->clientmutual) 00901 { 00902 err = shishi_ap_rep_build (state->ap); 00903 if (err) 00904 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00905 00906 err = shishi_ap_rep_der (state->ap, output, output_len); 00907 if (err) 00908 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00909 } 00910 else 00911 *output_len = 0; 00912 00913 return GSASL_OK; 00914 } 00915 } 00916 else 00917 { 00918 /* XXX Currently we only handle AS-REQ and AP-REQ in 00919 non-infrastructure mode. Supporting infrastructure mode is 00920 simple, just send the AS-REQ to the KDC and wait for AS-REP 00921 instead of creating AS-REP locally. 00922 00923 We should probably have a callback to decide policy: 00924 1) non-infrastructure mode (NIM) only 00925 2) infrastructure mode (IM) only 00926 3) proxied infrastructure mode (PIM) only 00927 4) NIM with fallback to IM (useful for local server overrides) 00928 5) IM with fallback to NIM (useful for admins if KDC is offline) 00929 6) ...etc with PIM too 00930 */ 00931 return GSASL_NEED_SERVER_RETRIEVE_CALLBACK; 00932 } 00933 00934 *output_len = 0; 00935 return GSASL_NEEDS_MORE; 00936 } 00937 00938 int 00939 _gsasl_kerberos_v5_server_encode (Gsasl_session * sctx, 00940 void *mech_data, 00941 const char *input, 00942 size_t input_len, 00943 char *output, size_t * output_len) 00944 { 00945 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 00946 int res; 00947 00948 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00949 { 00950 /* XXX */ 00951 } 00952 else if (state && state->sessionkey 00953 && state->clientqop & GSASL_QOP_AUTH_INT) 00954 { 00955 res = shishi_safe (state->sh, &state->safe); 00956 if (res != SHISHI_OK) 00957 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00958 00959 res = shishi_safe_set_user_data (state->sh, 00960 shishi_safe_safe (state->safe), 00961 input, input_len); 00962 if (res != SHISHI_OK) 00963 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00964 00965 res = shishi_safe_build (state->safe, state->sessionkey); 00966 if (res != SHISHI_OK) 00967 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00968 00969 res = shishi_safe_safe_der (state->safe, output, output_len); 00970 if (res != SHISHI_OK) 00971 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00972 } 00973 else 00974 { 00975 *output_len = input_len; 00976 if (output) 00977 memcpy (output, input, input_len); 00978 return GSASL_OK; 00979 } 00980 00981 return GSASL_OK; 00982 } 00983 00984 int 00985 _gsasl_kerberos_v5_server_decode (Gsasl_session * sctx, 00986 void *mech_data, 00987 const char *input, 00988 size_t input_len, 00989 char *output, size_t * output_len) 00990 { 00991 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 00992 int res; 00993 00994 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00995 { 00996 /* XXX */ 00997 } 00998 else if (state && state->sessionkey 00999 && state->clientqop & GSASL_QOP_AUTH_INT) 01000 { 01001 Shishi_asn1 asn1safe; 01002 01003 res = shishi_safe (state->sh, &state->safe); 01004 if (res != SHISHI_OK) 01005 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 01006 01007 res = shishi_safe_safe_der_set (state->safe, input, input_len); 01008 printf ("len %d err %d\n", input_len, res); 01009 if (res != SHISHI_OK) 01010 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 01011 01012 res = shishi_safe_verify (state->safe, state->sessionkey); 01013 if (res != SHISHI_OK) 01014 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 01015 01016 res = shishi_safe_user_data (state->sh, shishi_safe_safe (state->safe), 01017 output, output_len); 01018 if (res != SHISHI_OK) 01019 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 01020 printf ("len=%d\n", *output_len); 01021 return GSASL_OK; 01022 } 01023 else 01024 { 01025 *output_len = input_len; 01026 if (output) 01027 memcpy (output, input, input_len); 01028 return GSASL_OK; 01029 } 01030 01031 01032 return GSASL_OK; 01033 } 01034 01035 int 01036 _gsasl_kerberos_v5_server_finish (Gsasl_session * sctx, void *mech_data) 01037 { 01038 struct _Gsasl_kerberos_v5_server_state *state = mech_data; 01039 01040 shishi_done (state->sh); 01041 01042 free (state->username); 01043 free (state->password); 01044 free (state->random); 01045 free (state); 01046 01047 return GSASL_OK; 01048 } 01049 #endif /* USE_SERVER */
1.7.6.1