|
gsasl
1.8.0
|
00001 /* client.c --- Experimental SASL mechanism KERBEROS_V5, client 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_client_state 00031 { 00032 int step; 00033 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN]; 00034 int serverqops; 00035 int clientqop; 00036 int servermutual; 00037 uint32_t servermaxbuf; 00038 uint32_t clientmaxbuf; 00039 Shishi *sh; 00040 Shishi_tkt *tkt; 00041 Shishi_as *as; 00042 Shishi_ap *ap; 00043 Shishi_key *sessionkey; 00044 Shishi_safe *safe; 00045 }; 00046 00047 int 00048 _gsasl_kerberos_v5_client_init (Gsasl_ctx * ctx) 00049 { 00050 if (!shishi_check_version (SHISHI_VERSION)) 00051 return GSASL_UNKNOWN_MECHANISM; 00052 00053 return GSASL_OK; 00054 } 00055 00056 int 00057 _gsasl_kerberos_v5_client_start (Gsasl_session * sctx, void **mech_data) 00058 { 00059 struct _Gsasl_kerberos_v5_client_state *state; 00060 Gsasl_ctx *ctx; 00061 int err; 00062 00063 state = malloc (sizeof (*state)); 00064 if (state == NULL) 00065 return GSASL_MALLOC_ERROR; 00066 00067 memset (state, 0, sizeof (*state)); 00068 00069 err = shishi_init (&state->sh); 00070 if (err) 00071 return GSASL_KERBEROS_V5_INIT_ERROR; 00072 00073 state->step = 0; 00074 state->clientqop = GSASL_QOP_AUTH_INT; 00075 00076 *mech_data = state; 00077 00078 return GSASL_OK; 00079 } 00080 00081 #define STEP_FIRST 0 00082 #define STEP_NONINFRA_SEND_ASREQ 1 00083 #define STEP_NONINFRA_WAIT_ASREP 2 00084 #define STEP_NONINFRA_SEND_APREQ 3 00085 #define STEP_NONINFRA_WAIT_APREP 4 00086 #define STEP_SUCCESS 5 00087 00088 int 00089 _gsasl_kerberos_v5_client_step (Gsasl_session * sctx, 00090 void *mech_data, 00091 const char *input, 00092 size_t input_len, 00093 char *output, size_t * output_len) 00094 { 00095 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00096 Gsasl_client_callback_authentication_id cb_authentication_id; 00097 Gsasl_client_callback_authorization_id cb_authorization_id; 00098 Gsasl_client_callback_qop cb_qop; 00099 Gsasl_client_callback_realm cb_realm; 00100 Gsasl_client_callback_password cb_password; 00101 Gsasl_client_callback_service cb_service; 00102 Gsasl_client_callback_maxbuf cb_maxbuf; 00103 Gsasl_ctx *ctx; 00104 int res; 00105 int len; 00106 00107 ctx = gsasl_client_ctx_get (sctx); 00108 if (ctx == NULL) 00109 return GSASL_CANNOT_GET_CTX; 00110 00111 /* These are optional */ 00112 cb_realm = gsasl_client_callback_realm_get (ctx); 00113 cb_service = gsasl_client_callback_service_get (ctx); 00114 cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx); 00115 cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx); 00116 cb_qop = gsasl_client_callback_qop_get (ctx); 00117 cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx); 00118 00119 /* Only optionally needed in infrastructure mode */ 00120 cb_password = gsasl_client_callback_password_get (ctx); 00121 if (cb_password == NULL) 00122 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK; 00123 00124 /* I think we really need this one */ 00125 cb_service = gsasl_client_callback_service_get (ctx); 00126 if (cb_service == NULL) 00127 return GSASL_NEED_CLIENT_SERVICE_CALLBACK; 00128 00129 switch (state->step) 00130 { 00131 case STEP_FIRST: 00132 if (input == NULL) 00133 { 00134 *output_len = 0; 00135 return GSASL_NEEDS_MORE; 00136 } 00137 00138 if (input_len != SERVER_HELLO_LEN) 00139 return GSASL_MECHANISM_PARSE_ERROR; 00140 00141 memcpy (state->serverhello, input, input_len); 00142 00143 { 00144 unsigned char serverbitmap; 00145 00146 memcpy (&serverbitmap, input, BITMAP_LEN); 00147 state->serverqops = 0; 00148 if (serverbitmap & GSASL_QOP_AUTH) 00149 state->serverqops |= GSASL_QOP_AUTH; 00150 if (serverbitmap & GSASL_QOP_AUTH_INT) 00151 state->serverqops |= GSASL_QOP_AUTH_INT; 00152 if (serverbitmap & GSASL_QOP_AUTH_CONF) 00153 state->serverqops |= GSASL_QOP_AUTH_CONF; 00154 if (serverbitmap & MUTUAL) 00155 state->servermutual = 1; 00156 } 00157 memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN); 00158 state->servermaxbuf = ntohl (state->servermaxbuf); 00159 00160 if (cb_qop) 00161 state->clientqop = cb_qop (sctx, state->serverqops); 00162 00163 if (!(state->serverqops & state->clientqop & 00164 (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF))) 00165 return GSASL_AUTHENTICATION_ERROR; 00166 00167 /* XXX for now we require server authentication */ 00168 if (!state->servermutual) 00169 return GSASL_AUTHENTICATION_ERROR; 00170 00171 /* Decide policy here: non-infrastructure, infrastructure or proxy. 00172 * 00173 * A callback to decide should be added, but without the default 00174 * should be: 00175 * 00176 * IF shishi_tktset_get_for_server() THEN 00177 * INFRASTRUCTURE MODE 00178 * ELSE IF shishi_realm_for_server(server) THEN 00179 * PROXY INFRASTRUCTURE (then fallback to NIM?) 00180 * ELSE 00181 * NON-INFRASTRUCTURE MODE 00182 */ 00183 state->step = STEP_NONINFRA_SEND_APREQ; /* only NIM for now.. */ 00184 /* fall through */ 00185 00186 case STEP_NONINFRA_SEND_ASREQ: 00187 res = shishi_as (state->sh, &state->as); 00188 if (res) 00189 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00190 00191 if (cb_authentication_id) /* Shishi defaults to one otherwise */ 00192 { 00193 len = *output_len - 1; 00194 res = cb_authentication_id (sctx, output, &len); 00195 if (res != GSASL_OK) 00196 return res; 00197 output[len] = '\0'; 00198 00199 res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as), 00200 SHISHI_NT_UNKNOWN, output); 00201 if (res != GSASL_OK) 00202 return res; 00203 } 00204 00205 if (cb_realm) 00206 { 00207 len = *output_len - 1; 00208 res = cb_realm (sctx, output, &len); 00209 if (res != GSASL_OK) 00210 return res; 00211 } 00212 else 00213 len = 0; 00214 00215 output[len] = '\0'; 00216 res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as), 00217 output); 00218 if (res != GSASL_OK) 00219 return res; 00220 00221 if (cb_service) 00222 { 00223 char *sname[3]; 00224 size_t servicelen = 0; 00225 size_t hostnamelen = 0; 00226 00227 res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen, 00228 /* XXX support servicename a'la DIGEST-MD5 too? */ 00229 NULL, NULL); 00230 if (res != GSASL_OK) 00231 return res; 00232 00233 if (*output_len < servicelen + 1 + hostnamelen + 1) 00234 return GSASL_TOO_SMALL_BUFFER; 00235 00236 sname[0] = &output[0]; 00237 sname[1] = &output[servicelen + 2]; 00238 sname[2] = NULL; 00239 00240 res = cb_service (sctx, sname[0], &servicelen, 00241 sname[1], &hostnamelen, NULL, NULL); 00242 if (res != GSASL_OK) 00243 return res; 00244 00245 sname[0][servicelen] = '\0'; 00246 sname[1][hostnamelen] = '\0'; 00247 00248 res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as), 00249 SHISHI_NT_UNKNOWN, sname); 00250 if (res != GSASL_OK) 00251 return res; 00252 } 00253 00254 /* XXX query application for encryption types and set the etype 00255 field? Already configured by shishi though... */ 00256 00257 res = shishi_a2d (state->sh, shishi_as_req (state->as), 00258 output, output_len); 00259 if (res != SHISHI_OK) 00260 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00261 00262 state->step = STEP_NONINFRA_WAIT_ASREP; 00263 00264 res = GSASL_NEEDS_MORE; 00265 break; 00266 00267 case STEP_NONINFRA_WAIT_ASREP: 00268 if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK) 00269 return GSASL_MECHANISM_PARSE_ERROR; 00270 00271 /* XXX? password stored in callee's output buffer */ 00272 len = *output_len - 1; 00273 res = cb_password (sctx, output, &len); 00274 if (res != GSASL_OK && res != GSASL_NEEDS_MORE) 00275 return res; 00276 output[len] = '\0'; 00277 00278 res = shishi_as_rep_process (state->as, NULL, output); 00279 if (res != SHISHI_OK) 00280 return GSASL_AUTHENTICATION_ERROR; 00281 00282 state->step = STEP_NONINFRA_SEND_APREQ; 00283 /* fall through */ 00284 00285 case STEP_NONINFRA_SEND_APREQ: 00286 if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN) 00287 return GSASL_TOO_SMALL_BUFFER; 00288 00289 if (!(state->clientqop & ~GSASL_QOP_AUTH)) 00290 state->clientmaxbuf = 0; 00291 else if (cb_maxbuf) 00292 state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf); 00293 else 00294 state->clientmaxbuf = MAXBUF_DEFAULT; 00295 00296 /* XXX for now we require server authentication */ 00297 output[0] = state->clientqop | MUTUAL; 00298 { 00299 uint32_t tmp; 00300 00301 tmp = ntohl (state->clientmaxbuf); 00302 memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN); 00303 } 00304 memcpy (&output[CLIENT_HELLO_LEN], state->serverhello, 00305 SERVER_HELLO_LEN); 00306 00307 if (cb_authorization_id) 00308 { 00309 len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN; 00310 res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN + 00311 SERVER_HELLO_LEN], &len); 00312 } 00313 else 00314 len = 0; 00315 00316 len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN; 00317 res = shishi_ap_tktoptionsdata (state->sh, 00318 &state->ap, 00319 shishi_as_tkt (state->as), 00320 SHISHI_APOPTIONS_MUTUAL_REQUIRED, 00321 output, len); 00322 if (res != SHISHI_OK) 00323 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00324 00325 res = shishi_authenticator_add_authorizationdata 00326 (state->sh, shishi_ap_authenticator (state->ap), -1, output, len); 00327 if (res != SHISHI_OK) 00328 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00329 00330 /* XXX set realm in AP-REQ and Authenticator */ 00331 00332 res = shishi_ap_req_der (state->ap, output, output_len); 00333 if (res != SHISHI_OK) 00334 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00335 00336 state->step = STEP_NONINFRA_WAIT_APREP; 00337 00338 res = GSASL_NEEDS_MORE; 00339 break; 00340 00341 case STEP_NONINFRA_WAIT_APREP: 00342 if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK) 00343 return GSASL_MECHANISM_PARSE_ERROR; 00344 00345 res = shishi_ap_rep_verify (state->ap); 00346 if (res != SHISHI_OK) 00347 return GSASL_AUTHENTICATION_ERROR; 00348 00349 state->step = STEP_SUCCESS; 00350 00351 /* XXX support AP session keys */ 00352 state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as)); 00353 00354 *output_len = 0; 00355 res = GSASL_OK; 00356 break; 00357 00358 default: 00359 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; 00360 break; 00361 } 00362 00363 return res; 00364 } 00365 00366 int 00367 _gsasl_kerberos_v5_client_encode (Gsasl_session * sctx, 00368 void *mech_data, 00369 const char *input, 00370 size_t input_len, 00371 char **output, size_t * output_len) 00372 { 00373 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00374 int res; 00375 00376 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00377 { 00378 return GSASL_INTEGRITY_ERROR; 00379 } 00380 else if (state && state->sessionkey 00381 && state->clientqop & GSASL_QOP_AUTH_INT) 00382 { 00383 res = shishi_safe (state->sh, &state->safe); 00384 if (res != SHISHI_OK) 00385 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00386 00387 res = shishi_safe_set_user_data (state->sh, 00388 shishi_safe_safe (state->safe), 00389 input, input_len); 00390 if (res != SHISHI_OK) 00391 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00392 00393 res = shishi_safe_build (state->safe, state->sessionkey); 00394 if (res != SHISHI_OK) 00395 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00396 00397 res = shishi_safe_safe_der (state->safe, output, output_len); 00398 if (res != SHISHI_OK) 00399 return GSASL_KERBEROS_V5_INTERNAL_ERROR; 00400 } 00401 else 00402 { 00403 *output_len = input_len; 00404 *output = malloc (input_len); 00405 if (!*output) 00406 return GSASL_MALLOC_ERROR; 00407 memcpy (*output, input, input_len); 00408 } 00409 00410 return GSASL_OK; 00411 } 00412 00413 int 00414 _gsasl_kerberos_v5_client_decode (Gsasl_session * sctx, 00415 void *mech_data, 00416 const char *input, 00417 size_t input_len, 00418 char *output, size_t * output_len) 00419 { 00420 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00421 00422 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF) 00423 { 00424 return GSASL_INTEGRITY_ERROR; 00425 } 00426 else if (state && state->sessionkey 00427 && state->clientqop & GSASL_QOP_AUTH_INT) 00428 { 00429 return GSASL_INTEGRITY_ERROR; 00430 } 00431 else 00432 { 00433 *output_len = input_len; 00434 *output = malloc (input_len); 00435 if (!*output) 00436 return GSASL_MALLOC_ERROR; 00437 memcpy (*output, input, input_len); 00438 } 00439 00440 return GSASL_OK; 00441 } 00442 00443 int 00444 _gsasl_kerberos_v5_client_finish (Gsasl_session * sctx, void *mech_data) 00445 { 00446 struct _Gsasl_kerberos_v5_client_state *state = mech_data; 00447 00448 shishi_done (state->sh); 00449 free (state); 00450 00451 return GSASL_OK; 00452 }
1.7.6.1