imapext-2007

view 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 source
1 /* ========================================================================
2 * Copyright 1988-2006 University of Washington
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *
11 * ========================================================================
12 */
14 /*
15 * Program: GSSAPI authenticator
16 *
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
22 * Seattle, WA 98195
23 * Internet: MRC@CAC.Washington.EDU
24 *
25 * Date: 12 January 1998
26 * Last Edited: 30 August 2006
27 */
30 long auth_gssapi_valid (void);
31 long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
32 char *service,NETMBX *mb,void *stream,
33 unsigned long *trial,char *user);
34 long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
35 authrespond_t responder,char *service,NETMBX *mb,
36 void *stream,char *user,kinit_t ki);
37 char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]);
40 AUTHENTICATOR auth_gss = {
41 AU_SECURE | AU_AUTHUSER, /* secure authenticator */
42 "GSSAPI", /* authenticator name */
43 auth_gssapi_valid, /* check if valid */
44 auth_gssapi_client, /* client method */
45 auth_gssapi_server, /* server method */
46 NIL /* next authenticator */
47 };
49 #define AUTH_GSSAPI_P_NONE 1
50 #define AUTH_GSSAPI_P_INTEGRITY 2
51 #define AUTH_GSSAPI_P_PRIVACY 4
53 #define AUTH_GSSAPI_C_MAXSIZE 8192
55 #define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y)
57 /* Check if GSSAPI valid on this system
58 * Returns: T if valid, NIL otherwise
59 */
61 long auth_gssapi_valid (void)
62 {
63 char tmp[MAILTMPLEN];
64 OM_uint32 smn;
65 gss_buffer_desc buf;
66 gss_name_t name;
67 /* make service name */
68 sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
69 mylocalhost ());
70 buf.length = strlen (buf.value = tmp);
71 /* see if can build a name */
72 if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&name) !=
73 GSS_S_COMPLETE) return NIL;
74 /* remove server method if no keytab */
75 if (!kerberos_server_valid ()) auth_gss.server = NIL;
76 gss_release_name (&smn,&name);/* finished with name */
77 return LONGT;
78 }
80 /* Client authenticator
81 * Accepts: challenger function
82 * responder function
83 * SASL service name
84 * parsed network mailbox structure
85 * stream argument for functions
86 * pointer to current trial count
87 * returned user name
88 * Returns: T if success, NIL otherwise, number of trials incremented if retry
89 */
91 long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
92 char *service,NETMBX *mb,void *stream,
93 unsigned long *trial,char *user)
94 {
95 gss_buffer_desc chal;
96 kinit_t ki = (kinit_t) mail_parameters (NIL,GET_KINIT,NIL);
97 long ret = NIL;
98 *trial = 65535; /* never retry */
99 /* get initial (empty) challenge */
100 if (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) {
101 if (chal.length) { /* abort if challenge non-empty */
102 mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN);
103 (*responder) (stream,NIL,0);
104 ret = LONGT; /* will get a BAD response back */
105 }
106 else if (mb->authuser[0] && strcmp (mb->authuser,myusername ())) {
107 mm_log ("Can't use Kerberos: invalid /authuser",WARN);
108 (*responder) (stream,NIL,0);
109 ret = LONGT; /* will get a BAD response back */
110 }
111 else ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
112 stream,user,ki);
113 }
114 return ret;
115 }
117 /* Client authenticator worker function
118 * Accepts: challenger function
119 * responder function
120 * SASL service name
121 * parsed network mailbox structure
122 * stream argument for functions
123 * returned user name
124 * kinit function pointer if should retry with kinit
125 * Returns: T if success, NIL otherwise
126 */
128 long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
129 authrespond_t responder,char *service,NETMBX *mb,
130 void *stream,char *user,kinit_t ki)
131 {
132 char tmp[MAILTMPLEN];
133 OM_uint32 smj,smn,dsmj,dsmn;
134 OM_uint32 mctx = 0;
135 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
136 gss_buffer_desc resp,buf;
137 long i;
138 int conf;
139 gss_qop_t qop;
140 gss_name_t crname = NIL;
141 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
142 void *data;
143 long ret = NIL;
144 sprintf (tmp,"%s@%s",service,mb->host);
145 buf.length = strlen (buf.value = tmp);
146 /* get service name */
147 if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname) !=
148 GSS_S_COMPLETE) {
149 mm_log ("Can't import Kerberos service name",WARN);
150 (*responder) (stream,NIL,0);
151 }
152 else {
153 data = (*bn) (BLOCK_SENSITIVE,NIL);
154 /* negotiate with KDC */
155 smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,NIL,
156 GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG |
157 GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS,
158 GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL);
159 (*bn) (BLOCK_NONSENSITIVE,data);
161 /* while continuation needed */
162 while (smj == GSS_S_CONTINUE_NEEDED) {
163 if (chal.value) fs_give ((void **) &chal.value);
164 /* send response, get next challenge */
165 i = (*responder) (stream,resp.value,resp.length) &&
166 (chal.value = (*challenger) (stream,(unsigned long *) &chal.length));
167 gss_release_buffer (&smn,&resp);
168 if (i) { /* negotiate continuation with KDC */
169 data = (*bn) (BLOCK_SENSITIVE,NIL);
170 switch (smj = /* make sure continuation going OK */
171 gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,
172 crname,GSS_C_NO_OID,GSS_C_INTEG_FLAG |
173 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0,
174 GSS_C_NO_CHANNEL_BINDINGS,&chal,NIL,
175 &resp,NIL,NIL)) {
176 case GSS_S_CONTINUE_NEEDED:
177 case GSS_S_COMPLETE:
178 break;
179 default: /* error, don't need context any more */
180 gss_delete_sec_context (&smn,&ctx,NIL);
181 }
182 (*bn) (BLOCK_NONSENSITIVE,data);
183 }
184 else { /* error in continuation */
185 mm_log ("Error in negotiating Kerberos continuation",WARN);
186 (*responder) (stream,NIL,0);
187 /* don't need context any more */
188 gss_delete_sec_context (&smn,&ctx,NIL);
189 break;
190 }
191 }
193 switch (smj) { /* done - deal with final condition */
194 case GSS_S_COMPLETE:
195 if (chal.value) fs_give ((void **) &chal.value);
196 /* get prot mechanisms and max size */
197 if ((*responder) (stream,resp.value ? resp.value : "",resp.length) &&
198 (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&&
199 (gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) &&
200 (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){
201 /* make copy of flags and length */
202 memcpy (tmp,resp.value,4);
203 gss_release_buffer (&smn,&resp);
204 /* no session protection */
205 tmp[0] = AUTH_GSSAPI_P_NONE;
206 /* install user name */
207 strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ()));
208 buf.value = tmp; buf.length = strlen (user) + 4;
209 /* successful negotiation */
210 switch (smj = gss_wrap (&smn,ctx,NIL,qop,&buf,&conf,&resp)) {
211 case GSS_S_COMPLETE:
212 if ((*responder) (stream,resp.value,resp.length)) ret = T;
213 gss_release_buffer (&smn,&resp);
214 break;
215 default:
216 do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
217 GSS_C_NO_OID,&mctx,&resp)) {
218 case GSS_S_COMPLETE:
219 mctx = 0;
220 case GSS_S_CONTINUE_NEEDED:
221 sprintf (tmp,"Unknown gss_wrap failure: %s",(char *) resp.value);
222 mm_log (tmp,WARN);
223 gss_release_buffer (&dsmn,&resp);
224 }
225 while (dsmj == GSS_S_CONTINUE_NEEDED);
226 do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
227 GSS_C_NO_OID,&mctx,&resp)) {
228 case GSS_S_COMPLETE:
229 case GSS_S_CONTINUE_NEEDED:
230 sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
231 mm_log (tmp,WARN);
232 gss_release_buffer (&dsmn,&resp);
233 }
234 while (dsmj == GSS_S_CONTINUE_NEEDED);
235 (*responder) (stream,NIL,0);
236 }
237 }
238 /* flush final challenge */
239 if (chal.value) fs_give ((void **) &chal.value);
240 /* don't need context any more */
241 gss_delete_sec_context (&smn,&ctx,NIL);
242 break;
244 case GSS_S_CREDENTIALS_EXPIRED:
245 if (chal.value) fs_give ((void **) &chal.value);
246 /* retry if application kinits */
247 if (ki && (*ki) (mb->host,"Kerberos credentials expired"))
248 ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
249 stream,user,NIL);
250 else { /* application can't kinit */
251 sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s",
252 mb->host);
253 mm_log (tmp,WARN);
254 (*responder) (stream,NIL,0);
255 }
256 break;
257 case GSS_S_FAILURE:
258 if (chal.value) fs_give ((void **) &chal.value);
259 do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
260 GSS_C_NO_OID,&mctx,&resp)) {
261 case GSS_S_COMPLETE: /* end of message, can kinit? */
262 if (ki && kerberos_try_kinit (smn) &&
263 (*ki) (mb->host,(char *) resp.value)) {
264 gss_release_buffer (&dsmn,&resp);
265 ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
266 stream,user,NIL);
267 break; /* done */
268 }
269 else (*responder) (stream,NIL,0);
270 case GSS_S_CONTINUE_NEEDED:
271 sprintf (tmp,kerberos_try_kinit (smn) ?
272 "Kerberos error: %.80s (try running kinit) for %.80s" :
273 "GSSAPI failure: %s for %.80s",(char *) resp.value,mb->host);
274 mm_log (tmp,WARN);
275 gss_release_buffer (&dsmn,&resp);
276 } while (dsmj == GSS_S_CONTINUE_NEEDED);
277 break;
279 default: /* miscellaneous errors */
280 if (chal.value) fs_give ((void **) &chal.value);
281 do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
282 GSS_C_NO_OID,&mctx,&resp)) {
283 case GSS_S_COMPLETE:
284 mctx = 0;
285 case GSS_S_CONTINUE_NEEDED:
286 sprintf (tmp,"Unknown GSSAPI failure: %s",(char *) resp.value);
287 mm_log (tmp,WARN);
288 gss_release_buffer (&dsmn,&resp);
289 }
290 while (dsmj == GSS_S_CONTINUE_NEEDED);
291 do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
292 GSS_C_NO_OID,&mctx,&resp)) {
293 case GSS_S_COMPLETE:
294 case GSS_S_CONTINUE_NEEDED:
295 sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
296 mm_log (tmp,WARN);
297 gss_release_buffer (&dsmn,&resp);
298 }
299 while (dsmj == GSS_S_CONTINUE_NEEDED);
300 (*responder) (stream,NIL,0);
301 break;
302 }
303 /* finished with credentials name */
304 if (crname) gss_release_name (&smn,&crname);
305 }
306 return ret; /* return status */
307 }
309 /* Server authenticator
310 * Accepts: responder function
311 * argument count
312 * argument vector
313 * Returns: authenticated user name or NIL
314 */
316 char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[])
317 {
318 char *ret = NIL;
319 char tmp[MAILTMPLEN];
320 unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE);
321 int conf;
322 OM_uint32 smj,smn,dsmj,dsmn,flags;
323 OM_uint32 mctx = 0;
324 gss_name_t crname,name;
325 gss_OID mech;
326 gss_buffer_desc chal,resp,buf;
327 gss_cred_id_t crd;
328 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
329 gss_qop_t qop = GSS_C_QOP_DEFAULT;
330 /* make service name */
331 sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
332 tcp_serverhost ());
333 buf.length = strlen (buf.value = tmp);
334 /* acquire credentials */
335 if ((gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname)) ==
336 GSS_S_COMPLETE) {
337 if ((smj = gss_acquire_cred (&smn,crname,0,NIL,GSS_C_ACCEPT,&crd,NIL,NIL))
338 == GSS_S_COMPLETE) {
339 if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) {
340 do { /* negotiate authentication */
341 smj = gss_accept_sec_context (&smn,&ctx,crd,&resp,
342 GSS_C_NO_CHANNEL_BINDINGS,&name,&mech,
343 &chal,&flags,NIL,NIL);
344 /* don't need response any more */
345 fs_give ((void **) &resp.value);
346 switch (smj) { /* how did it go? */
347 case GSS_S_COMPLETE: /* successful */
348 case GSS_S_CONTINUE_NEEDED:
349 if (chal.value) { /* send challenge, get next response */
350 resp.value = (*responder) (chal.value,chal.length,
351 (unsigned long *) &resp.length);
352 gss_release_buffer (&smn,&chal);
353 }
354 break;
355 }
356 }
357 while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED));
359 /* successful exchange? */
360 if ((smj == GSS_S_COMPLETE) &&
361 (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) {
362 /* send security and size */
363 memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4);
364 tmp[0] = AUTH_GSSAPI_P_NONE;
365 if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){
366 resp.value = (*responder) (chal.value,chal.length,
367 (unsigned long *) &resp.length);
368 gss_release_buffer (&smn,&chal);
369 if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) {
370 /* client request valid */
371 if (chal.value && (chal.length > 4) &&
372 (chal.length < (MAILTMPLEN - 1)) &&
373 memcpy (tmp,chal.value,chal.length) &&
374 (tmp[0] & AUTH_GSSAPI_P_NONE)) {
375 /* tie off authorization ID */
376 tmp[chal.length] = '\0';
377 ret = kerberos_login (tmp+4,buf.value,argc,argv);
378 }
379 /* done with user name */
380 gss_release_buffer (&smn,&chal);
381 }
382 /* finished with response */
383 fs_give ((void **) &resp.value);
384 }
385 /* don't need name buffer any more */
386 gss_release_buffer (&smn,&buf);
387 }
388 /* don't need client name any more */
389 gss_release_name (&smn,&name);
390 /* don't need context any more */
391 if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL);
392 }
393 /* finished with credentials */
394 gss_release_cred (&smn,&crd);
395 }
397 else { /* can't acquire credentials! */
398 if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE)
399 SERVER_LOG ("Failed to acquire credentials for %s",buf.value);
400 if (smj != GSS_S_FAILURE) do
401 switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
402 GSS_C_NO_OID,&mctx,&resp)) {
403 case GSS_S_COMPLETE:
404 mctx = 0;
405 case GSS_S_CONTINUE_NEEDED:
406 SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value);
407 gss_release_buffer (&dsmn,&resp);
408 }
409 while (dsmj == GSS_S_CONTINUE_NEEDED);
410 do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
411 GSS_C_NO_OID,&mctx,&resp)) {
412 case GSS_S_COMPLETE:
413 case GSS_S_CONTINUE_NEEDED:
414 SERVER_LOG ("GSSAPI mechanism status: %s",resp.value);
415 gss_release_buffer (&dsmn,&resp);
416 }
417 while (dsmj == GSS_S_CONTINUE_NEEDED);
418 }
419 /* finished with credentials name */
420 gss_release_name (&smn,&crname);
421 }
422 return ret; /* return status */
423 }

UW-IMAP'd extensions by yuuji