1 /* ========================================================================
2 * Copyright 2020-2022 Eduardo Chappa
3 * Copyright 1988-2006 University of Washington
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * Program: GSSAPI authenticator
18 * Author: Mark Crispin
19 * Networks and Distributed Computing
20 * Computing & Communications
21 * University of Washington
22 * Administration Building, AG-44
24 * Internet: MRC@CAC.Washington.EDU
26 * Date: 12 January 1998
27 * Last Edited: 30 August 2006
31 long auth_gssapi_valid (void);
32 long auth_gssapi_client (authchallenge_t challenger
,authrespond_t responder
, char *base
,
33 char *service
,NETMBX
*mb
,void *stream
, unsigned long port
,
34 unsigned long *trial
,char *user
);
35 long auth_gssapi_client_work (authchallenge_t challenger
,gss_buffer_desc chal
,
36 authrespond_t responder
,char *service
,NETMBX
*mb
,
37 void *stream
,char *user
,kinit_t ki
);
38 char *auth_gssapi_server (authresponse_t responder
,int argc
,char *argv
[]);
41 AUTHENTICATOR auth_gss
= {
42 AU_SECURE
| AU_AUTHUSER
, /* secure authenticator */
43 "GSSAPI", /* authenticator name */
44 auth_gssapi_valid
, /* check if valid */
45 auth_gssapi_client
, /* client method */
46 auth_gssapi_server
, /* server method */
47 NIL
/* next authenticator */
50 #define AUTH_GSSAPI_P_NONE 1
51 #define AUTH_GSSAPI_P_INTEGRITY 2
52 #define AUTH_GSSAPI_P_PRIVACY 4
54 #define AUTH_GSSAPI_C_MAXSIZE 8192
56 #define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y)
58 /* Check if GSSAPI valid on this system
59 * Returns: T if valid, NIL otherwise
62 long auth_gssapi_valid (void)
68 /* make service name */
69 sprintf (tmp
,"%s@%s",(char *) mail_parameters (NIL
,GET_SERVICENAME
,NIL
),
71 buf
.length
= strlen (buf
.value
= tmp
);
72 /* see if can build a name */
73 if (gss_import_name (&smn
,&buf
,GSS_C_NT_HOSTBASED_SERVICE
,&name
) !=
74 GSS_S_COMPLETE
) return NIL
;
75 /* remove server method if no keytab */
76 if (!kerberos_server_valid ()) auth_gss
.server
= NIL
;
77 gss_release_name (&smn
,&name
);/* finished with name */
81 /* Client authenticator
82 * Accepts: challenger function
85 * parsed network mailbox structure
86 * stream argument for functions
87 * pointer to current trial count
89 * Returns: T if success, NIL otherwise, number of trials incremented if retry
92 long auth_gssapi_client (authchallenge_t challenger
,authrespond_t
94 char *service
,NETMBX
*mb
,void *stream
,unsigned long port
,
95 unsigned long *trial
,char *user
)
98 kinit_t ki
= (kinit_t
) mail_parameters (NIL
,GET_KINIT
,NIL
);
100 *trial
= 65535; /* never retry */
101 /* get initial (empty) challenge */
102 if ((chal
.value
= (*challenger
) (stream
,(unsigned long *) &chal
.length
)) != NULL
) {
103 #if 0 /* ignore non-empty challenge */
104 if (chal
.length
) { /* abort if challenge non-empty */
105 mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN
);
106 (*responder
) (stream
,NIL
,NIL
,0);
107 ret
= LONGT
; /* will get a BAD response back */
111 if (mb
->authuser
[0] && strcmp (mb
->authuser
,myusername ())) {
112 mm_log ("Can't use Kerberos: invalid /authuser",WARN
);
113 (*responder
) (stream
,NIL
,NIL
,0);
114 ret
= LONGT
; /* will get a BAD response back */
116 else ret
= auth_gssapi_client_work (challenger
,chal
,responder
,service
,mb
,
122 /* Client authenticator worker function
123 * Accepts: challenger function
126 * parsed network mailbox structure
127 * stream argument for functions
129 * kinit function pointer if should retry with kinit
130 * Returns: T if success, NIL otherwise
133 long auth_gssapi_client_work (authchallenge_t challenger
,gss_buffer_desc chal
,
134 authrespond_t responder
,char *service
,NETMBX
*mb
,
135 void *stream
,char *user
,kinit_t ki
)
137 char tmp
[MAILTMPLEN
];
138 OM_uint32 smj
,smn
,dsmj
,dsmn
;
140 gss_ctx_id_t ctx
= GSS_C_NO_CONTEXT
;
141 gss_buffer_desc resp
,buf
;
145 gss_name_t crname
= NIL
;
146 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
149 sprintf (tmp
,"%s@%s",service
,mb
->host
);
150 buf
.length
= strlen (buf
.value
= tmp
);
151 /* get service name */
152 if (gss_import_name (&smn
,&buf
,GSS_C_NT_HOSTBASED_SERVICE
,&crname
) !=
154 mm_log ("Can't import Kerberos service name",WARN
);
155 (*responder
) (stream
,NIL
,NIL
,0);
158 data
= (*bn
) (BLOCK_SENSITIVE
,NIL
);
159 /* negotiate with KDC */
160 smj
= gss_init_sec_context (&smn
,GSS_C_NO_CREDENTIAL
,&ctx
,crname
,NIL
,
161 GSS_C_INTEG_FLAG
| GSS_C_MUTUAL_FLAG
|
162 GSS_C_REPLAY_FLAG
,0,GSS_C_NO_CHANNEL_BINDINGS
,
163 GSS_C_NO_BUFFER
,NIL
,&resp
,NIL
,NIL
);
164 (*bn
) (BLOCK_NONSENSITIVE
,data
);
166 /* while continuation needed */
167 while (smj
== GSS_S_CONTINUE_NEEDED
) {
168 if (chal
.value
) fs_give ((void **) &chal
.value
);
169 /* send response, get next challenge */
170 i
= (*responder
) (stream
,NIL
,resp
.value
,resp
.length
) &&
171 (chal
.value
= (*challenger
) (stream
,(unsigned long *) &chal
.length
));
172 gss_release_buffer (&smn
,&resp
);
173 if (i
) { /* negotiate continuation with KDC */
174 data
= (*bn
) (BLOCK_SENSITIVE
,NIL
);
175 switch (smj
= /* make sure continuation going OK */
176 gss_init_sec_context (&smn
,GSS_C_NO_CREDENTIAL
,&ctx
,
177 crname
,GSS_C_NO_OID
,GSS_C_INTEG_FLAG
|
178 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,0,
179 GSS_C_NO_CHANNEL_BINDINGS
,&chal
,NIL
,
181 case GSS_S_CONTINUE_NEEDED
:
184 default: /* error, don't need context any more */
185 gss_delete_sec_context (&smn
,&ctx
,NIL
);
187 (*bn
) (BLOCK_NONSENSITIVE
,data
);
189 else { /* error in continuation */
190 mm_log ("Error in negotiating Kerberos continuation",WARN
);
191 (*responder
) (stream
,NIL
,NIL
,0);
192 /* don't need context any more */
193 gss_delete_sec_context (&smn
,&ctx
,NIL
);
198 switch (smj
) { /* done - deal with final condition */
200 if (chal
.value
) fs_give ((void **) &chal
.value
);
201 /* get prot mechanisms and max size */
202 if ((*responder
) (stream
,NIL
,resp
.value
? resp
.value
: "",resp
.length
) &&
203 (chal
.value
= (*challenger
) (stream
,(unsigned long *)&chal
.length
))&&
204 (gss_unwrap (&smn
,ctx
,&chal
,&resp
,&conf
,&qop
) == GSS_S_COMPLETE
) &&
205 (resp
.length
>= 4) && (*((char *) resp
.value
) & AUTH_GSSAPI_P_NONE
)){
206 /* make copy of flags and length */
207 memcpy (tmp
,resp
.value
,4);
208 gss_release_buffer (&smn
,&resp
);
209 /* no session protection */
210 tmp
[0] = AUTH_GSSAPI_P_NONE
;
211 /* install user name */
212 strcpy (tmp
+4,strcpy (user
,mb
->user
[0] ? mb
->user
: myusername ()));
213 buf
.value
= tmp
; buf
.length
= strlen (user
) + 4;
214 /* successful negotiation */
215 switch (smj
= gss_wrap (&smn
,ctx
,NIL
,qop
,&buf
,&conf
,&resp
)) {
217 if ((*responder
) (stream
,NIL
,resp
.value
,resp
.length
)) ret
= T
;
218 gss_release_buffer (&smn
,&resp
);
221 do switch (dsmj
= gss_display_status (&dsmn
,smj
,GSS_C_GSS_CODE
,
222 GSS_C_NO_OID
,&mctx
,&resp
)) {
225 case GSS_S_CONTINUE_NEEDED
:
226 sprintf (tmp
,"Unknown gss_wrap failure: %s",(char *) resp
.value
);
228 gss_release_buffer (&dsmn
,&resp
);
230 while (dsmj
== GSS_S_CONTINUE_NEEDED
);
231 do switch (dsmj
= gss_display_status (&dsmn
,smn
,GSS_C_MECH_CODE
,
232 GSS_C_NO_OID
,&mctx
,&resp
)) {
234 case GSS_S_CONTINUE_NEEDED
:
235 sprintf (tmp
,"GSSAPI mechanism status: %s",(char *) resp
.value
);
237 gss_release_buffer (&dsmn
,&resp
);
239 while (dsmj
== GSS_S_CONTINUE_NEEDED
);
240 (*responder
) (stream
,NIL
,NIL
,0);
243 /* flush final challenge */
244 if (chal
.value
) fs_give ((void **) &chal
.value
);
245 /* don't need context any more */
246 gss_delete_sec_context (&smn
,&ctx
,NIL
);
249 case GSS_S_CREDENTIALS_EXPIRED
:
250 if (chal
.value
) fs_give ((void **) &chal
.value
);
251 /* retry if application kinits */
252 if (ki
&& (*ki
) (mb
->host
,"Kerberos credentials expired"))
253 ret
= auth_gssapi_client_work (challenger
,chal
,responder
,service
,mb
,
255 else { /* application can't kinit */
256 sprintf (tmp
,"Kerberos credentials expired (try running kinit) for %s",
259 (*responder
) (stream
,NIL
,NIL
,0);
263 if (chal
.value
) fs_give ((void **) &chal
.value
);
264 do switch (dsmj
= gss_display_status (&dsmn
,smn
,GSS_C_MECH_CODE
,
265 GSS_C_NO_OID
,&mctx
,&resp
)) {
266 case GSS_S_COMPLETE
: /* end of message, can kinit? */
267 if (ki
&& kerberos_try_kinit (smn
) &&
268 (*ki
) (mb
->host
,(char *) resp
.value
)) {
269 gss_release_buffer (&dsmn
,&resp
);
270 ret
= auth_gssapi_client_work (challenger
,chal
,responder
,service
,mb
,
274 else (*responder
) (stream
,NIL
,NIL
,0);
275 case GSS_S_CONTINUE_NEEDED
:
276 sprintf (tmp
,kerberos_try_kinit (smn
) ?
277 "Kerberos error: %.80s (try running kinit) for %.80s" :
278 "GSSAPI failure: %s for %.80s",(char *) resp
.value
,mb
->host
);
280 gss_release_buffer (&dsmn
,&resp
);
281 } while (dsmj
== GSS_S_CONTINUE_NEEDED
);
284 default: /* miscellaneous errors */
285 if (chal
.value
) fs_give ((void **) &chal
.value
);
286 do switch (dsmj
= gss_display_status (&dsmn
,smj
,GSS_C_GSS_CODE
,
287 GSS_C_NO_OID
,&mctx
,&resp
)) {
290 case GSS_S_CONTINUE_NEEDED
:
291 sprintf (tmp
,"Unknown GSSAPI failure: %s",(char *) resp
.value
);
293 gss_release_buffer (&dsmn
,&resp
);
295 while (dsmj
== GSS_S_CONTINUE_NEEDED
);
296 do switch (dsmj
= gss_display_status (&dsmn
,smn
,GSS_C_MECH_CODE
,
297 GSS_C_NO_OID
,&mctx
,&resp
)) {
299 case GSS_S_CONTINUE_NEEDED
:
300 sprintf (tmp
,"GSSAPI mechanism status: %s",(char *) resp
.value
);
302 gss_release_buffer (&dsmn
,&resp
);
304 while (dsmj
== GSS_S_CONTINUE_NEEDED
);
305 (*responder
) (stream
,NIL
,NIL
,0);
308 /* finished with credentials name */
309 if (crname
) gss_release_name (&smn
,&crname
);
311 return ret
; /* return status */
314 /* Server authenticator
315 * Accepts: responder function
318 * Returns: authenticated user name or NIL
321 char *auth_gssapi_server (authresponse_t responder
,int argc
,char *argv
[])
324 char tmp
[MAILTMPLEN
];
325 unsigned long maxsize
= htonl (AUTH_GSSAPI_C_MAXSIZE
);
327 OM_uint32 smj
,smn
,dsmj
,dsmn
,flags
;
329 gss_name_t crname
,name
;
331 gss_buffer_desc chal
,resp
,buf
;
333 gss_ctx_id_t ctx
= GSS_C_NO_CONTEXT
;
334 gss_qop_t qop
= GSS_C_QOP_DEFAULT
;
335 /* make service name */
336 sprintf (tmp
,"%s@%s",(char *) mail_parameters (NIL
,GET_SERVICENAME
,NIL
),
338 buf
.length
= strlen (buf
.value
= tmp
);
339 /* acquire credentials */
340 if ((gss_import_name (&smn
,&buf
,GSS_C_NT_HOSTBASED_SERVICE
,&crname
)) ==
342 if ((smj
= gss_acquire_cred (&smn
,crname
,0,NIL
,GSS_C_ACCEPT
,&crd
,NIL
,NIL
))
344 if ((resp
.value
= (*responder
) ("",0,(unsigned long *) &resp
.length
)) != NULL
) {
345 do { /* negotiate authentication */
346 smj
= gss_accept_sec_context (&smn
,&ctx
,crd
,&resp
,
347 GSS_C_NO_CHANNEL_BINDINGS
,&name
,&mech
,
348 &chal
,&flags
,NIL
,NIL
);
349 /* don't need response any more */
350 fs_give ((void **) &resp
.value
);
351 switch (smj
) { /* how did it go? */
352 case GSS_S_COMPLETE
: /* successful */
353 case GSS_S_CONTINUE_NEEDED
:
354 if (chal
.value
) { /* send challenge, get next response */
355 resp
.value
= (*responder
) (chal
.value
,chal
.length
,
356 (unsigned long *) &resp
.length
);
357 gss_release_buffer (&smn
,&chal
);
362 while (resp
.value
&& resp
.length
&& (smj
== GSS_S_CONTINUE_NEEDED
));
364 /* successful exchange? */
365 if ((smj
== GSS_S_COMPLETE
) &&
366 (gss_display_name (&smn
,name
,&buf
,&mech
) == GSS_S_COMPLETE
)) {
367 /* send security and size */
368 memcpy (resp
.value
= tmp
,(void *) &maxsize
,resp
.length
= 4);
369 tmp
[0] = AUTH_GSSAPI_P_NONE
;
370 if (gss_wrap (&smn
,ctx
,NIL
,qop
,&resp
,&conf
,&chal
) == GSS_S_COMPLETE
){
371 resp
.value
= (*responder
) (chal
.value
,chal
.length
,
372 (unsigned long *) &resp
.length
);
373 gss_release_buffer (&smn
,&chal
);
374 if (gss_unwrap (&smn
,ctx
,&resp
,&chal
,&conf
,&qop
)==GSS_S_COMPLETE
) {
375 /* client request valid */
376 if (chal
.value
&& (chal
.length
> 4) &&
377 (chal
.length
< (MAILTMPLEN
- 1)) &&
378 memcpy (tmp
,chal
.value
,chal
.length
) &&
379 (tmp
[0] & AUTH_GSSAPI_P_NONE
)) {
380 /* tie off authorization ID */
381 tmp
[chal
.length
] = '\0';
382 ret
= kerberos_login (tmp
+4,buf
.value
,argc
,argv
);
384 /* done with user name */
385 gss_release_buffer (&smn
,&chal
);
387 /* finished with response */
388 fs_give ((void **) &resp
.value
);
390 /* don't need name buffer any more */
391 gss_release_buffer (&smn
,&buf
);
393 /* don't need client name any more */
394 gss_release_name (&smn
,&name
);
395 /* don't need context any more */
396 if (ctx
!= GSS_C_NO_CONTEXT
) gss_delete_sec_context (&smn
,&ctx
,NIL
);
398 /* finished with credentials */
399 gss_release_cred (&smn
,&crd
);
402 else { /* can't acquire credentials! */
403 if (gss_display_name (&dsmn
,crname
,&buf
,&mech
) == GSS_S_COMPLETE
)
404 SERVER_LOG ("Failed to acquire credentials for %s",(char *) buf
.value
);
405 if (smj
!= GSS_S_FAILURE
) do
406 switch (dsmj
= gss_display_status (&dsmn
,smj
,GSS_C_GSS_CODE
,
407 GSS_C_NO_OID
,&mctx
,&resp
)) {
410 case GSS_S_CONTINUE_NEEDED
:
411 SERVER_LOG ("Unknown GSSAPI failure: %s",(char *)resp
.value
);
412 gss_release_buffer (&dsmn
,&resp
);
414 while (dsmj
== GSS_S_CONTINUE_NEEDED
);
415 do switch (dsmj
= gss_display_status (&dsmn
,smn
,GSS_C_MECH_CODE
,
416 GSS_C_NO_OID
,&mctx
,&resp
)) {
418 case GSS_S_CONTINUE_NEEDED
:
419 SERVER_LOG ("GSSAPI mechanism status: %s",(char *)resp
.value
);
420 gss_release_buffer (&dsmn
,&resp
);
422 while (dsmj
== GSS_S_CONTINUE_NEEDED
);
424 /* finished with credentials name */
425 gss_release_name (&smn
,&crname
);
427 return ret
; /* return status */