1 /* ========================================================================
2 * Copyright 2006 University of Washington
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
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * ========================================================================
14 /* #define PROTOTYPE(x) x */
19 #include "wp_uidmapper_lib.h"
21 #include <gssapi/gssapi_generic.h>
22 #include <gssapi/gssapi_krb5.h>
24 #define AUTH_GSS_PROXY_MESSAGE 1
25 #define AUTH_GSS_PROXY_READ 2
26 #define AUTH_GSS_PROXY_SUCCESS 3
27 #define AUTH_GSS_PROXY_WRITE 4
28 #define AUTH_GSS_PROXY_WRITE_NIL 5
30 #define AUTH_GSSAPI_P_NONE 1
31 #define AUTH_GSSAPI_P_INTEGRITY 2
32 #define AUTH_GSSAPI_P_PRIVACY 4
35 int get_calling_username(int uid
,char *name
,int namelen
) {
41 len
= strlen(pw
->pw_name
);
42 if(len
>= namelen
) len
= namelen
- 1;
43 memcpy(name
,pw
->pw_name
,len
);
48 #define get_calling_username wp_uidmapper_getname
51 static unsigned long read_full(int fd
,void *buf
,unsigned long size
) {
52 unsigned long total
,s
;
53 for(total
= 0; total
< size
; total
+= s
) {
54 s
= read(fd
,(char*)buf
+ total
,size
- total
);
56 if((errno
== EAGAIN
) || (errno
== EINTR
)) s
= 0;
58 } else if(s
== 0) break;
63 static unsigned long write_full(int fd
,void *buf
,unsigned long size
) {
64 unsigned long total
,s
;
65 for(total
= 0; total
< size
; total
+= s
) {
66 s
= write(fd
,(char*)buf
+ total
,size
- total
);
68 if((errno
== EAGAIN
) || (errno
== EINTR
)) s
= 0;
75 int cmd_message(char *str1
, ...) {
77 unsigned long cmd
[2],size
;
80 for(size
= 0,str
= str1
,va_start(list
,str1
); str
; str
= va_arg(list
,char*))
84 cmd
[0] = AUTH_GSS_PROXY_MESSAGE
;
86 if(write_full(1,cmd
,sizeof(cmd
)) == -1) return -1;
87 for(str
= str1
, va_start(list
,str1
); str
; str
= va_arg(list
,char*))
88 if(size
= strlen(str
)) if(write_full(1,str
,size
) == -1) {
96 int cmd_read(gss_buffer_desc
*pbuf
) {
97 unsigned long cmd
[2],len
,size
;
100 cmd
[0] = AUTH_GSS_PROXY_READ
;
102 if(write_full(1,cmd
,sizeof(cmd
)) == -1) return -1;
104 len
= read_full(0,&size
,sizeof(size
));
105 if(len
!= sizeof(size
)) return -1;
113 len
= read_full(0,buf
,size
);
124 unsigned long cmd
[2];
125 cmd
[0] = AUTH_GSS_PROXY_SUCCESS
;
127 if(write_full(1,cmd
,sizeof(cmd
)) == -1) return -1;
131 int cmd_write(gss_buffer_desc
*buf
) {
132 unsigned long cmd
[2];
133 cmd
[0] = AUTH_GSS_PROXY_WRITE
;
134 cmd
[1] = buf
->length
;
135 if(write_full(1,cmd
,sizeof(cmd
)) == -1) return -1;
136 if(buf
->length
) if(write_full(1,buf
->value
,buf
->length
) == -1) return -1;
140 int cmd_write_nil() {
141 unsigned long cmd
[2];
142 cmd
[0] = AUTH_GSS_PROXY_WRITE_NIL
;
144 if(write_full(1,cmd
,sizeof(cmd
)) == -1) return -1;
149 * service principal in argv[1]
150 * requested username in argv[2]
153 int main(int argc
,char *argv
[])
156 char userbuf
[WP_BUF_SIZE
];
158 OM_uint32 smj
,smn
,dsmn
,mctx
;
159 gss_name_t crname
= GSS_C_NO_NAME
;
160 gss_ctx_id_t ctx
= GSS_C_NO_CONTEXT
;
161 gss_buffer_desc chal
,resp
,buf
;
165 int calling_uid
,eff_uid
;
167 if((prog
= strrchr(argv
[0], '/')) == NULL
)
172 openlog(prog
,LOG_PID
,LOG_MAIL
);
174 calling_uid
= getuid();
177 syslog(LOG_INFO
,"uid = %d, euid=%d\n",calling_uid
,eff_uid
);
181 /* if euid != uid, change to web server user */
182 if(calling_uid
!= eff_uid
) if(setuid(WEBSERVER_UID
)) {
183 syslog(LOG_ERR
,"setuid ((%d != %d) -> %d) failed: %s",
184 calling_uid
,eff_uid
,WEBSERVER_UID
,strerror(errno
));
191 syslog(LOG_WARNING
,"not enough arguments");
195 if(get_calling_username(calling_uid
,userbuf
,WP_BUF_SIZE
) == -1) {
196 syslog(LOG_WARNING
,"cannot determine calling username");
203 syslog(LOG_INFO
,"calling=%s\n",user
);
205 } else if(argc
> 2) {
208 syslog(LOG_INFO
,"requested=%s calling=%s\n",user
,userbuf
);
210 #ifndef NO_NAME_CHECK
211 if(strcmp(user
,userbuf
)) {
212 syslog(LOG_WARNING
,"cannot act on behalf of user %s (%s)",user
,userbuf
);
219 /* expect empty challenge from server */
220 if(cmd_read(&chal
)) {
221 syslog(LOG_WARNING
,"cmd_read[initial] failed");
223 } else if(chal
.length
) {
225 syslog(LOG_WARNING
,"cmd_read[initial] not empty");
230 * obtain credentials for requested service
234 buf
.length
= strlen(argv
[1]);
235 if(gss_import_name (&smn
,&buf
,gss_nt_service_name
,&crname
) !=
237 syslog(LOG_WARNING
,"gss_import_name(%s) failed",buf
.value
);
242 /* initial init_sec_context call, and send data */
243 memcpy(&oid
,&gss_mech_krb5
,sizeof(oid
));
244 smj
= gss_init_sec_context
245 (&smn
,GSS_C_NO_CREDENTIAL
,&ctx
,crname
,oid
,
246 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,0,GSS_C_NO_CHANNEL_BINDINGS
,
247 GSS_C_NO_BUFFER
,0,&resp
,0,0);
248 if((smj
== GSS_S_COMPLETE
) || (smj
== GSS_S_CONTINUE_NEEDED
)) {
249 i
= cmd_write(&resp
);
250 gss_release_buffer (&smn
,&resp
);
252 syslog(LOG_WARNING
,"cmd_write[init_sec_context] failed");
257 /* loop until init_sec_context is done */
258 while(smj
== GSS_S_CONTINUE_NEEDED
) {
259 if(cmd_read(&chal
)) {
260 syslog(LOG_WARNING
,"cmd_read[init_sec_context] failed");
262 } else if(!chal
.length
) {
263 syslog(LOG_WARNING
,"cmd_read[init_sec_context] empty");
266 smj
= gss_init_sec_context (&smn
,GSS_C_NO_CREDENTIAL
,&ctx
,crname
,
268 GSS_C_MUTUAL_FLAG
|GSS_C_REPLAY_FLAG
,0,
269 GSS_C_NO_CHANNEL_BINDINGS
,&chal
,0,
271 if(chal
.value
) free(chal
.value
);
272 if((smj
== GSS_S_COMPLETE
) || (smj
== GSS_S_CONTINUE_NEEDED
)) {
273 i
= cmd_write(&resp
);
274 gss_release_buffer (&smn
,&resp
);
276 syslog(LOG_WARNING
,"cmd_write[init_sec_context] failed");
285 /* get challenge and unwrap it */
286 if(cmd_read(&chal
)) {
287 syslog(LOG_WARNING
,"cmd_read[gss_unwrap] failed");
290 smj
= gss_unwrap (&smn
,ctx
,&chal
,&resp
,&conf
,&qop
);
291 if(chal
.value
) free(chal
.value
);
292 if(smj
!= GSS_S_COMPLETE
) {
293 syslog(LOG_WARNING
,"gss_unwrap failed");
296 } else if(resp
.length
< 4) {
297 syslog(LOG_WARNING
,"challenge too short");
298 gss_release_buffer (&smn
,&resp
);
301 } else if(!( ((char*)resp
.value
)[0] & AUTH_GSSAPI_P_NONE
)) {
302 syslog(LOG_WARNING
,"invalid challenge");
303 gss_release_buffer (&smn
,&resp
);
308 /* prepare response to challenge */
309 buf
.length
= 4 + (user
? strlen(user
) : 0);
310 buf
.value
= malloc(buf
.length
);
311 memcpy (buf
.value
,resp
.value
,4);
312 gss_release_buffer (&smn
,&resp
);
313 *(char*)buf
.value
= AUTH_GSSAPI_P_NONE
;
314 if(user
) memcpy((char*)buf
.value
+ 4, user
, buf
.length
- 4);
316 /* wrap response and send */
317 smj
= gss_wrap (&smn
,ctx
,0,qop
,&buf
,&conf
,&resp
);
319 if(smj
!= GSS_S_COMPLETE
) {
320 syslog(LOG_WARNING
,"gss_unwrap failed");
324 i
= cmd_write(&resp
);
325 gss_release_buffer (&smn
,&resp
);
327 syslog(LOG_WARNING
,"cmd_write[gss_wrap] failed");
332 if(cmd_success()) syslog(LOG_WARNING
,"cmd_success failed");
335 case GSS_S_CREDENTIALS_EXPIRED
:
337 syslog(LOG_INFO
,"Kerberos credentials expired (try running kinit)");
339 if(cmd_message("Kerberos credentials expired (try running kinit)",0)) {
340 syslog(LOG_WARNING
,"cmd_message[credentials expired] failed");
347 if (smn
== (OM_uint32
) KRB5_FCC_NOFILE
) {
349 syslog(LOG_INFO
,"No credentials cache found (try running kinit)");
351 if(cmd_message("No credentials cache found (try running kinit)",0)) {
352 syslog(LOG_WARNING
,"cmd_message[no cache file found] failed");
358 gss_display_status (&dsmn
,smn
,GSS_C_MECH_CODE
,
359 GSS_C_NO_OID
,&mctx
,&resp
);
361 syslog(LOG_INFO
,"GSSAPI failure: %s",resp
.value
);
363 i
= cmd_message("GSSAPI failure: ",resp
.value
,0);
364 gss_release_buffer (&dsmn
,&resp
);
366 syslog(LOG_WARNING
,"cmd_message[failure] failed");
377 gss_display_status (&dsmn
,smn
,GSS_C_GSS_CODE
,
378 GSS_C_NO_OID
,&mctx
,&resp
);
380 syslog(LOG_INFO
,"GSSAPI failure: %s",resp
.value
);
382 i
= cmd_message("Unknown GSSAPI failure: ",resp
.value
,0);
383 gss_release_buffer (&dsmn
,&resp
);
385 syslog(LOG_WARNING
,"cmd_message[unknown failure] failed");
390 gss_display_status (&dsmn
,smn
,GSS_C_MECH_CODE
,
391 GSS_C_NO_OID
,&mctx
,&resp
);
393 syslog(LOG_INFO
,"GSSAPI mechanism status: %s",resp
.value
);
395 i
= cmd_message("GSSAPI mechanism status: ",resp
.value
,0);
396 gss_release_buffer (&dsmn
,&resp
);
398 syslog(LOG_WARNING
,"cmd_message[unknown failure 2] failed");
407 if(ctx
!= GSS_C_NO_CONTEXT
) gss_delete_sec_context (&smn
,&ctx
,0);
408 if(crname
!= GSS_C_NO_NAME
) gss_release_name (&smn
,&crname
);