imapext-2007
diff src/c-client/auth_gss.c @ 0:ada5e610ab86
imap-2007e
author | yuuji@gentei.org |
---|---|
date | Mon, 14 Sep 2009 15:17:45 +0900 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/c-client/auth_gss.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,423 @@ 1.4 +/* ======================================================================== 1.5 + * Copyright 1988-2006 University of Washington 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * 1.14 + * ======================================================================== 1.15 + */ 1.16 + 1.17 +/* 1.18 + * Program: GSSAPI authenticator 1.19 + * 1.20 + * Author: Mark Crispin 1.21 + * Networks and Distributed Computing 1.22 + * Computing & Communications 1.23 + * University of Washington 1.24 + * Administration Building, AG-44 1.25 + * Seattle, WA 98195 1.26 + * Internet: MRC@CAC.Washington.EDU 1.27 + * 1.28 + * Date: 12 January 1998 1.29 + * Last Edited: 30 August 2006 1.30 + */ 1.31 + 1.32 + 1.33 +long auth_gssapi_valid (void); 1.34 +long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder, 1.35 + char *service,NETMBX *mb,void *stream, 1.36 + unsigned long *trial,char *user); 1.37 +long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal, 1.38 + authrespond_t responder,char *service,NETMBX *mb, 1.39 + void *stream,char *user,kinit_t ki); 1.40 +char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]); 1.41 + 1.42 + 1.43 +AUTHENTICATOR auth_gss = { 1.44 + AU_SECURE | AU_AUTHUSER, /* secure authenticator */ 1.45 + "GSSAPI", /* authenticator name */ 1.46 + auth_gssapi_valid, /* check if valid */ 1.47 + auth_gssapi_client, /* client method */ 1.48 + auth_gssapi_server, /* server method */ 1.49 + NIL /* next authenticator */ 1.50 +}; 1.51 + 1.52 +#define AUTH_GSSAPI_P_NONE 1 1.53 +#define AUTH_GSSAPI_P_INTEGRITY 2 1.54 +#define AUTH_GSSAPI_P_PRIVACY 4 1.55 + 1.56 +#define AUTH_GSSAPI_C_MAXSIZE 8192 1.57 + 1.58 +#define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y) 1.59 + 1.60 +/* Check if GSSAPI valid on this system 1.61 + * Returns: T if valid, NIL otherwise 1.62 + */ 1.63 + 1.64 +long auth_gssapi_valid (void) 1.65 +{ 1.66 + char tmp[MAILTMPLEN]; 1.67 + OM_uint32 smn; 1.68 + gss_buffer_desc buf; 1.69 + gss_name_t name; 1.70 + /* make service name */ 1.71 + sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL), 1.72 + mylocalhost ()); 1.73 + buf.length = strlen (buf.value = tmp); 1.74 + /* see if can build a name */ 1.75 + if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&name) != 1.76 + GSS_S_COMPLETE) return NIL; 1.77 + /* remove server method if no keytab */ 1.78 + if (!kerberos_server_valid ()) auth_gss.server = NIL; 1.79 + gss_release_name (&smn,&name);/* finished with name */ 1.80 + return LONGT; 1.81 +} 1.82 + 1.83 +/* Client authenticator 1.84 + * Accepts: challenger function 1.85 + * responder function 1.86 + * SASL service name 1.87 + * parsed network mailbox structure 1.88 + * stream argument for functions 1.89 + * pointer to current trial count 1.90 + * returned user name 1.91 + * Returns: T if success, NIL otherwise, number of trials incremented if retry 1.92 + */ 1.93 + 1.94 +long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder, 1.95 + char *service,NETMBX *mb,void *stream, 1.96 + unsigned long *trial,char *user) 1.97 +{ 1.98 + gss_buffer_desc chal; 1.99 + kinit_t ki = (kinit_t) mail_parameters (NIL,GET_KINIT,NIL); 1.100 + long ret = NIL; 1.101 + *trial = 65535; /* never retry */ 1.102 + /* get initial (empty) challenge */ 1.103 + if (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) { 1.104 + if (chal.length) { /* abort if challenge non-empty */ 1.105 + mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN); 1.106 + (*responder) (stream,NIL,0); 1.107 + ret = LONGT; /* will get a BAD response back */ 1.108 + } 1.109 + else if (mb->authuser[0] && strcmp (mb->authuser,myusername ())) { 1.110 + mm_log ("Can't use Kerberos: invalid /authuser",WARN); 1.111 + (*responder) (stream,NIL,0); 1.112 + ret = LONGT; /* will get a BAD response back */ 1.113 + } 1.114 + else ret = auth_gssapi_client_work (challenger,chal,responder,service,mb, 1.115 + stream,user,ki); 1.116 + } 1.117 + return ret; 1.118 +} 1.119 + 1.120 +/* Client authenticator worker function 1.121 + * Accepts: challenger function 1.122 + * responder function 1.123 + * SASL service name 1.124 + * parsed network mailbox structure 1.125 + * stream argument for functions 1.126 + * returned user name 1.127 + * kinit function pointer if should retry with kinit 1.128 + * Returns: T if success, NIL otherwise 1.129 + */ 1.130 + 1.131 +long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal, 1.132 + authrespond_t responder,char *service,NETMBX *mb, 1.133 + void *stream,char *user,kinit_t ki) 1.134 +{ 1.135 + char tmp[MAILTMPLEN]; 1.136 + OM_uint32 smj,smn,dsmj,dsmn; 1.137 + OM_uint32 mctx = 0; 1.138 + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 1.139 + gss_buffer_desc resp,buf; 1.140 + long i; 1.141 + int conf; 1.142 + gss_qop_t qop; 1.143 + gss_name_t crname = NIL; 1.144 + blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); 1.145 + void *data; 1.146 + long ret = NIL; 1.147 + sprintf (tmp,"%s@%s",service,mb->host); 1.148 + buf.length = strlen (buf.value = tmp); 1.149 + /* get service name */ 1.150 + if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname) != 1.151 + GSS_S_COMPLETE) { 1.152 + mm_log ("Can't import Kerberos service name",WARN); 1.153 + (*responder) (stream,NIL,0); 1.154 + } 1.155 + else { 1.156 + data = (*bn) (BLOCK_SENSITIVE,NIL); 1.157 + /* negotiate with KDC */ 1.158 + smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,NIL, 1.159 + GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG | 1.160 + GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS, 1.161 + GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL); 1.162 + (*bn) (BLOCK_NONSENSITIVE,data); 1.163 + 1.164 + /* while continuation needed */ 1.165 + while (smj == GSS_S_CONTINUE_NEEDED) { 1.166 + if (chal.value) fs_give ((void **) &chal.value); 1.167 + /* send response, get next challenge */ 1.168 + i = (*responder) (stream,resp.value,resp.length) && 1.169 + (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)); 1.170 + gss_release_buffer (&smn,&resp); 1.171 + if (i) { /* negotiate continuation with KDC */ 1.172 + data = (*bn) (BLOCK_SENSITIVE,NIL); 1.173 + switch (smj = /* make sure continuation going OK */ 1.174 + gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx, 1.175 + crname,GSS_C_NO_OID,GSS_C_INTEG_FLAG | 1.176 + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0, 1.177 + GSS_C_NO_CHANNEL_BINDINGS,&chal,NIL, 1.178 + &resp,NIL,NIL)) { 1.179 + case GSS_S_CONTINUE_NEEDED: 1.180 + case GSS_S_COMPLETE: 1.181 + break; 1.182 + default: /* error, don't need context any more */ 1.183 + gss_delete_sec_context (&smn,&ctx,NIL); 1.184 + } 1.185 + (*bn) (BLOCK_NONSENSITIVE,data); 1.186 + } 1.187 + else { /* error in continuation */ 1.188 + mm_log ("Error in negotiating Kerberos continuation",WARN); 1.189 + (*responder) (stream,NIL,0); 1.190 + /* don't need context any more */ 1.191 + gss_delete_sec_context (&smn,&ctx,NIL); 1.192 + break; 1.193 + } 1.194 + } 1.195 + 1.196 + switch (smj) { /* done - deal with final condition */ 1.197 + case GSS_S_COMPLETE: 1.198 + if (chal.value) fs_give ((void **) &chal.value); 1.199 + /* get prot mechanisms and max size */ 1.200 + if ((*responder) (stream,resp.value ? resp.value : "",resp.length) && 1.201 + (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&& 1.202 + (gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) && 1.203 + (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){ 1.204 + /* make copy of flags and length */ 1.205 + memcpy (tmp,resp.value,4); 1.206 + gss_release_buffer (&smn,&resp); 1.207 + /* no session protection */ 1.208 + tmp[0] = AUTH_GSSAPI_P_NONE; 1.209 + /* install user name */ 1.210 + strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ())); 1.211 + buf.value = tmp; buf.length = strlen (user) + 4; 1.212 + /* successful negotiation */ 1.213 + switch (smj = gss_wrap (&smn,ctx,NIL,qop,&buf,&conf,&resp)) { 1.214 + case GSS_S_COMPLETE: 1.215 + if ((*responder) (stream,resp.value,resp.length)) ret = T; 1.216 + gss_release_buffer (&smn,&resp); 1.217 + break; 1.218 + default: 1.219 + do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE, 1.220 + GSS_C_NO_OID,&mctx,&resp)) { 1.221 + case GSS_S_COMPLETE: 1.222 + mctx = 0; 1.223 + case GSS_S_CONTINUE_NEEDED: 1.224 + sprintf (tmp,"Unknown gss_wrap failure: %s",(char *) resp.value); 1.225 + mm_log (tmp,WARN); 1.226 + gss_release_buffer (&dsmn,&resp); 1.227 + } 1.228 + while (dsmj == GSS_S_CONTINUE_NEEDED); 1.229 + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, 1.230 + GSS_C_NO_OID,&mctx,&resp)) { 1.231 + case GSS_S_COMPLETE: 1.232 + case GSS_S_CONTINUE_NEEDED: 1.233 + sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value); 1.234 + mm_log (tmp,WARN); 1.235 + gss_release_buffer (&dsmn,&resp); 1.236 + } 1.237 + while (dsmj == GSS_S_CONTINUE_NEEDED); 1.238 + (*responder) (stream,NIL,0); 1.239 + } 1.240 + } 1.241 + /* flush final challenge */ 1.242 + if (chal.value) fs_give ((void **) &chal.value); 1.243 + /* don't need context any more */ 1.244 + gss_delete_sec_context (&smn,&ctx,NIL); 1.245 + break; 1.246 + 1.247 + case GSS_S_CREDENTIALS_EXPIRED: 1.248 + if (chal.value) fs_give ((void **) &chal.value); 1.249 + /* retry if application kinits */ 1.250 + if (ki && (*ki) (mb->host,"Kerberos credentials expired")) 1.251 + ret = auth_gssapi_client_work (challenger,chal,responder,service,mb, 1.252 + stream,user,NIL); 1.253 + else { /* application can't kinit */ 1.254 + sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s", 1.255 + mb->host); 1.256 + mm_log (tmp,WARN); 1.257 + (*responder) (stream,NIL,0); 1.258 + } 1.259 + break; 1.260 + case GSS_S_FAILURE: 1.261 + if (chal.value) fs_give ((void **) &chal.value); 1.262 + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, 1.263 + GSS_C_NO_OID,&mctx,&resp)) { 1.264 + case GSS_S_COMPLETE: /* end of message, can kinit? */ 1.265 + if (ki && kerberos_try_kinit (smn) && 1.266 + (*ki) (mb->host,(char *) resp.value)) { 1.267 + gss_release_buffer (&dsmn,&resp); 1.268 + ret = auth_gssapi_client_work (challenger,chal,responder,service,mb, 1.269 + stream,user,NIL); 1.270 + break; /* done */ 1.271 + } 1.272 + else (*responder) (stream,NIL,0); 1.273 + case GSS_S_CONTINUE_NEEDED: 1.274 + sprintf (tmp,kerberos_try_kinit (smn) ? 1.275 + "Kerberos error: %.80s (try running kinit) for %.80s" : 1.276 + "GSSAPI failure: %s for %.80s",(char *) resp.value,mb->host); 1.277 + mm_log (tmp,WARN); 1.278 + gss_release_buffer (&dsmn,&resp); 1.279 + } while (dsmj == GSS_S_CONTINUE_NEEDED); 1.280 + break; 1.281 + 1.282 + default: /* miscellaneous errors */ 1.283 + if (chal.value) fs_give ((void **) &chal.value); 1.284 + do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE, 1.285 + GSS_C_NO_OID,&mctx,&resp)) { 1.286 + case GSS_S_COMPLETE: 1.287 + mctx = 0; 1.288 + case GSS_S_CONTINUE_NEEDED: 1.289 + sprintf (tmp,"Unknown GSSAPI failure: %s",(char *) resp.value); 1.290 + mm_log (tmp,WARN); 1.291 + gss_release_buffer (&dsmn,&resp); 1.292 + } 1.293 + while (dsmj == GSS_S_CONTINUE_NEEDED); 1.294 + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, 1.295 + GSS_C_NO_OID,&mctx,&resp)) { 1.296 + case GSS_S_COMPLETE: 1.297 + case GSS_S_CONTINUE_NEEDED: 1.298 + sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value); 1.299 + mm_log (tmp,WARN); 1.300 + gss_release_buffer (&dsmn,&resp); 1.301 + } 1.302 + while (dsmj == GSS_S_CONTINUE_NEEDED); 1.303 + (*responder) (stream,NIL,0); 1.304 + break; 1.305 + } 1.306 + /* finished with credentials name */ 1.307 + if (crname) gss_release_name (&smn,&crname); 1.308 + } 1.309 + return ret; /* return status */ 1.310 +} 1.311 + 1.312 +/* Server authenticator 1.313 + * Accepts: responder function 1.314 + * argument count 1.315 + * argument vector 1.316 + * Returns: authenticated user name or NIL 1.317 + */ 1.318 + 1.319 +char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]) 1.320 +{ 1.321 + char *ret = NIL; 1.322 + char tmp[MAILTMPLEN]; 1.323 + unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE); 1.324 + int conf; 1.325 + OM_uint32 smj,smn,dsmj,dsmn,flags; 1.326 + OM_uint32 mctx = 0; 1.327 + gss_name_t crname,name; 1.328 + gss_OID mech; 1.329 + gss_buffer_desc chal,resp,buf; 1.330 + gss_cred_id_t crd; 1.331 + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 1.332 + gss_qop_t qop = GSS_C_QOP_DEFAULT; 1.333 + /* make service name */ 1.334 + sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL), 1.335 + tcp_serverhost ()); 1.336 + buf.length = strlen (buf.value = tmp); 1.337 + /* acquire credentials */ 1.338 + if ((gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname)) == 1.339 + GSS_S_COMPLETE) { 1.340 + if ((smj = gss_acquire_cred (&smn,crname,0,NIL,GSS_C_ACCEPT,&crd,NIL,NIL)) 1.341 + == GSS_S_COMPLETE) { 1.342 + if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) { 1.343 + do { /* negotiate authentication */ 1.344 + smj = gss_accept_sec_context (&smn,&ctx,crd,&resp, 1.345 + GSS_C_NO_CHANNEL_BINDINGS,&name,&mech, 1.346 + &chal,&flags,NIL,NIL); 1.347 + /* don't need response any more */ 1.348 + fs_give ((void **) &resp.value); 1.349 + switch (smj) { /* how did it go? */ 1.350 + case GSS_S_COMPLETE: /* successful */ 1.351 + case GSS_S_CONTINUE_NEEDED: 1.352 + if (chal.value) { /* send challenge, get next response */ 1.353 + resp.value = (*responder) (chal.value,chal.length, 1.354 + (unsigned long *) &resp.length); 1.355 + gss_release_buffer (&smn,&chal); 1.356 + } 1.357 + break; 1.358 + } 1.359 + } 1.360 + while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED)); 1.361 + 1.362 + /* successful exchange? */ 1.363 + if ((smj == GSS_S_COMPLETE) && 1.364 + (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) { 1.365 + /* send security and size */ 1.366 + memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4); 1.367 + tmp[0] = AUTH_GSSAPI_P_NONE; 1.368 + if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){ 1.369 + resp.value = (*responder) (chal.value,chal.length, 1.370 + (unsigned long *) &resp.length); 1.371 + gss_release_buffer (&smn,&chal); 1.372 + if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) { 1.373 + /* client request valid */ 1.374 + if (chal.value && (chal.length > 4) && 1.375 + (chal.length < (MAILTMPLEN - 1)) && 1.376 + memcpy (tmp,chal.value,chal.length) && 1.377 + (tmp[0] & AUTH_GSSAPI_P_NONE)) { 1.378 + /* tie off authorization ID */ 1.379 + tmp[chal.length] = '\0'; 1.380 + ret = kerberos_login (tmp+4,buf.value,argc,argv); 1.381 + } 1.382 + /* done with user name */ 1.383 + gss_release_buffer (&smn,&chal); 1.384 + } 1.385 + /* finished with response */ 1.386 + fs_give ((void **) &resp.value); 1.387 + } 1.388 + /* don't need name buffer any more */ 1.389 + gss_release_buffer (&smn,&buf); 1.390 + } 1.391 + /* don't need client name any more */ 1.392 + gss_release_name (&smn,&name); 1.393 + /* don't need context any more */ 1.394 + if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL); 1.395 + } 1.396 + /* finished with credentials */ 1.397 + gss_release_cred (&smn,&crd); 1.398 + } 1.399 + 1.400 + else { /* can't acquire credentials! */ 1.401 + if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE) 1.402 + SERVER_LOG ("Failed to acquire credentials for %s",buf.value); 1.403 + if (smj != GSS_S_FAILURE) do 1.404 + switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE, 1.405 + GSS_C_NO_OID,&mctx,&resp)) { 1.406 + case GSS_S_COMPLETE: 1.407 + mctx = 0; 1.408 + case GSS_S_CONTINUE_NEEDED: 1.409 + SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value); 1.410 + gss_release_buffer (&dsmn,&resp); 1.411 + } 1.412 + while (dsmj == GSS_S_CONTINUE_NEEDED); 1.413 + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, 1.414 + GSS_C_NO_OID,&mctx,&resp)) { 1.415 + case GSS_S_COMPLETE: 1.416 + case GSS_S_CONTINUE_NEEDED: 1.417 + SERVER_LOG ("GSSAPI mechanism status: %s",resp.value); 1.418 + gss_release_buffer (&dsmn,&resp); 1.419 + } 1.420 + while (dsmj == GSS_S_CONTINUE_NEEDED); 1.421 + } 1.422 + /* finished with credentials name */ 1.423 + gss_release_name (&smn,&crname); 1.424 + } 1.425 + return ret; /* return status */ 1.426 +}