* clear out some warnings by gcc 9.3.1.
[alpine.git] / web / src / pubcookie / wp_gssapi_proxy.c
blob4fd6dca46c1a43b93a63491f45618b533b53161f
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 */
16 #include <system.h>
17 #include <general.h>
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
34 #ifdef NO_UIDMAPPER
35 int get_calling_username(int uid,char *name,int namelen) {
36 struct passwd *pw;
37 unsigned long len;
39 pw = getpwuid(uid);
40 if(!pw) return -1;
41 len = strlen(pw->pw_name);
42 if(len >= namelen) len = namelen - 1;
43 memcpy(name,pw->pw_name,len);
44 name[len] = 0;
45 return len;
47 #else
48 #define get_calling_username wp_uidmapper_getname
49 #endif
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);
55 if(s == -1) {
56 if((errno == EAGAIN) || (errno == EINTR)) s = 0;
57 else return -1;
58 } else if(s == 0) break;
60 return total;
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);
67 if(s == -1) {
68 if((errno == EAGAIN) || (errno == EINTR)) s = 0;
69 else return -1;
72 return total;
75 int cmd_message(char *str1, ...) {
76 va_list list;
77 unsigned long cmd[2],size;
78 char *str;
80 for(size = 0,str = str1,va_start(list,str1); str; str = va_arg(list,char*))
81 size += strlen(str);
82 va_end(list);
84 cmd[0] = AUTH_GSS_PROXY_MESSAGE;
85 cmd[1] = size;
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) {
89 va_end(list);
90 return -1;
92 va_end(list);
93 return 0;
96 int cmd_read(gss_buffer_desc *pbuf) {
97 unsigned long cmd[2],len,size;
98 void *buf;
100 cmd[0] = AUTH_GSS_PROXY_READ;
101 cmd[1] = 0;
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;
106 if(size == 0) {
107 pbuf->value = 0;
108 pbuf->length = 0;
109 return 0;
112 buf = malloc(size);
113 len = read_full(0,buf,size);
114 if(len != size) {
115 free(buf);
116 return -1;
118 pbuf->value = buf;
119 pbuf->length = size;
120 return 0;
123 int cmd_success() {
124 unsigned long cmd[2];
125 cmd[0] = AUTH_GSS_PROXY_SUCCESS;
126 cmd[1] = 0;
127 if(write_full(1,cmd,sizeof(cmd)) == -1) return -1;
128 return 0;
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;
137 return 0;
140 int cmd_write_nil() {
141 unsigned long cmd[2];
142 cmd[0] = AUTH_GSS_PROXY_WRITE_NIL;
143 cmd[1] = 0;
144 if(write_full(1,cmd,sizeof(cmd)) == -1) return -1;
145 return 0;
149 * service principal in argv[1]
150 * requested username in argv[2]
153 int main(int argc,char *argv[])
155 char *user = 0;
156 char userbuf[WP_BUF_SIZE];
157 char *prog;
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;
162 int conf,i;
163 gss_qop_t qop;
164 gss_OID oid;
165 int calling_uid,eff_uid;
167 if((prog = strrchr(argv[0], '/')) == NULL)
168 prog = argv[0];
169 else
170 prog++;
172 openlog(prog,LOG_PID,LOG_MAIL);
174 calling_uid = getuid();
175 eff_uid = geteuid();
176 #ifdef DEBUG
177 syslog(LOG_INFO,"uid = %d, euid=%d\n",calling_uid,eff_uid);
178 #endif
180 #ifdef WEBSERVER_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));
185 cmd_write_nil();
186 goto cleanup;
188 #endif
190 if(argc < 2) {
191 syslog(LOG_WARNING,"not enough arguments");
192 cmd_write_nil();
193 goto cleanup;
195 if(get_calling_username(calling_uid,userbuf,WP_BUF_SIZE) == -1) {
196 syslog(LOG_WARNING,"cannot determine calling username");
197 cmd_write_nil();
198 goto cleanup;
200 if(argc == 2) {
201 user = userbuf;
202 #ifdef DEBUG
203 syslog(LOG_INFO,"calling=%s\n",user);
204 #endif
205 } else if(argc > 2) {
206 user = argv[2];
207 #ifdef DEBUG
208 syslog(LOG_INFO,"requested=%s calling=%s\n",user,userbuf);
209 #endif
210 #ifndef NO_NAME_CHECK
211 if(strcmp(user,userbuf)) {
212 syslog(LOG_WARNING,"cannot act on behalf of user %s (%s)",user,userbuf);
213 cmd_write_nil();
214 goto cleanup;
216 #endif
219 /* expect empty challenge from server */
220 if(cmd_read(&chal)) {
221 syslog(LOG_WARNING,"cmd_read[initial] failed");
222 goto cleanup;
223 } else if(chal.length) {
224 free(chal.value);
225 syslog(LOG_WARNING,"cmd_read[initial] not empty");
226 goto cleanup;
230 * obtain credentials for requested service
233 buf.value = argv[1];
234 buf.length = strlen(argv[1]);
235 if(gss_import_name (&smn,&buf,gss_nt_service_name,&crname) !=
236 GSS_S_COMPLETE) {
237 syslog(LOG_WARNING,"gss_import_name(%s) failed",buf.value);
238 cmd_write_nil();
239 goto cleanup;
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);
251 if(i) {
252 syslog(LOG_WARNING,"cmd_write[init_sec_context] failed");
253 goto cleanup;
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");
261 goto cleanup;
262 } else if(!chal.length) {
263 syslog(LOG_WARNING,"cmd_read[init_sec_context] empty");
264 goto cleanup;
265 } else {
266 smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,
267 GSS_C_NO_OID,
268 GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG,0,
269 GSS_C_NO_CHANNEL_BINDINGS,&chal,0,
270 &resp,0,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);
275 if(i) {
276 syslog(LOG_WARNING,"cmd_write[init_sec_context] failed");
277 goto cleanup;
283 switch(smj) {
284 case GSS_S_COMPLETE:
285 /* get challenge and unwrap it */
286 if(cmd_read(&chal)) {
287 syslog(LOG_WARNING,"cmd_read[gss_unwrap] failed");
288 goto cleanup;
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");
294 cmd_write_nil();
295 goto cleanup;
296 } else if(resp.length < 4) {
297 syslog(LOG_WARNING,"challenge too short");
298 gss_release_buffer (&smn,&resp);
299 cmd_write_nil();
300 goto cleanup;
301 } else if(!( ((char*)resp.value)[0] & AUTH_GSSAPI_P_NONE)) {
302 syslog(LOG_WARNING,"invalid challenge");
303 gss_release_buffer (&smn,&resp);
304 cmd_write_nil();
305 goto cleanup;
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);
318 free(buf.value);
319 if(smj != GSS_S_COMPLETE) {
320 syslog(LOG_WARNING,"gss_unwrap failed");
321 cmd_write_nil();
322 goto cleanup;
324 i = cmd_write(&resp);
325 gss_release_buffer (&smn,&resp);
326 if(i) {
327 syslog(LOG_WARNING,"cmd_write[gss_wrap] failed");
328 goto cleanup;
331 /* success! */
332 if(cmd_success()) syslog(LOG_WARNING,"cmd_success failed");
333 goto cleanup;
335 case GSS_S_CREDENTIALS_EXPIRED:
336 #ifdef DEBUG
337 syslog(LOG_INFO,"Kerberos credentials expired (try running kinit)");
338 #endif
339 if(cmd_message("Kerberos credentials expired (try running kinit)",0)) {
340 syslog(LOG_WARNING,"cmd_message[credentials expired] failed");
341 goto cleanup;
343 cmd_write_nil();
344 goto cleanup;
346 case GSS_S_FAILURE:
347 if (smn == (OM_uint32) KRB5_FCC_NOFILE) {
348 #ifdef DEBUG
349 syslog(LOG_INFO,"No credentials cache found (try running kinit)");
350 #endif
351 if(cmd_message("No credentials cache found (try running kinit)",0)) {
352 syslog(LOG_WARNING,"cmd_message[no cache file found] failed");
353 goto cleanup;
355 } else {
356 mctx = 0;
357 do {
358 gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
359 GSS_C_NO_OID,&mctx,&resp);
360 #ifdef DEBUG
361 syslog(LOG_INFO,"GSSAPI failure: %s",resp.value);
362 #endif
363 i = cmd_message("GSSAPI failure: ",resp.value,0);
364 gss_release_buffer (&dsmn,&resp);
365 if(i) {
366 syslog(LOG_WARNING,"cmd_message[failure] failed");
367 goto cleanup;
369 } while(mctx);
371 cmd_write_nil();
372 goto cleanup;
374 default:
375 mctx = 0;
376 do {
377 gss_display_status (&dsmn,smn,GSS_C_GSS_CODE,
378 GSS_C_NO_OID,&mctx,&resp);
379 #ifdef DEBUG
380 syslog(LOG_INFO,"GSSAPI failure: %s",resp.value);
381 #endif
382 i = cmd_message("Unknown GSSAPI failure: ",resp.value,0);
383 gss_release_buffer (&dsmn,&resp);
384 if(i) {
385 syslog(LOG_WARNING,"cmd_message[unknown failure] failed");
386 goto cleanup;
388 } while(mctx);
389 if(!mctx) do {
390 gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
391 GSS_C_NO_OID,&mctx,&resp);
392 #ifdef DEBUG
393 syslog(LOG_INFO,"GSSAPI mechanism status: %s",resp.value);
394 #endif
395 i = cmd_message("GSSAPI mechanism status: ",resp.value,0);
396 gss_release_buffer (&dsmn,&resp);
397 if(i) {
398 syslog(LOG_WARNING,"cmd_message[unknown failure 2] failed");
399 goto cleanup;
401 } while(mctx);
402 cmd_write_nil();
403 goto cleanup;
406 cleanup:
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);
409 closelog();
410 exit(0);
411 return 0;