diff options
author | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
---|---|---|
committer | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
commit | 094ca96844842928810f14844413109fc6cdd890 (patch) | |
tree | e60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /web/src/pubcookie/auth_gss_proxy.c | |
download | alpine-094ca96844842928810f14844413109fc6cdd890.tar.xz |
Initial Alpine Version
Diffstat (limited to 'web/src/pubcookie/auth_gss_proxy.c')
-rw-r--r-- | web/src/pubcookie/auth_gss_proxy.c | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/web/src/pubcookie/auth_gss_proxy.c b/web/src/pubcookie/auth_gss_proxy.c new file mode 100644 index 00000000..ea5daf69 --- /dev/null +++ b/web/src/pubcookie/auth_gss_proxy.c @@ -0,0 +1,380 @@ +/* ======================================================================== + * Copyright 2006-2008 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + +#include <system.h> +#include "../../../c-client/mail.h" +#include "../../../c-client/misc.h" +#include "../../../c-client/osdep.h" + +#define PROTOTYPE(x) x +#include <gssapi/gssapi_generic.h> +#include <gssapi/gssapi_krb5.h> + +long auth_gssapi_proxy_valid (void); +long auth_gssapi_proxy_client (authchallenge_t challenger, + authrespond_t responder, + char *service, + NETMBX *mb,void *stream,unsigned long *trial, + char *user); +char *auth_gssapi_proxy_server (authresponse_t responder,int argc,char *argv[]); + +AUTHENTICATOR auth_gss_proxy = { + AU_SECURE | AU_AUTHUSER, /* secure authenticator */ + "GSSAPI", /* authenticator name */ + auth_gssapi_proxy_valid, /* check if valid */ + auth_gssapi_proxy_client, /* client method */ + auth_gssapi_proxy_server, /* server method */ + NIL /* next authenticator */ +}; + +#define AUTH_GSSAPI_P_NONE 1 +#define AUTH_GSSAPI_P_INTEGRITY 2 +#define AUTH_GSSAPI_P_PRIVACY 4 + +#define AUTH_GSSAPI_C_MAXSIZE 8192 + +#define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y) + +extern char *krb5_defkeyname; /* sneaky way to get this name */ + + +/* Placate const declarations */ + +static gss_OID auth_gss_proxy_mech; +static gss_OID_set auth_gss_proxy_mech_set; + +/* Check if GSSAPI valid on this system + * Returns: T if valid, NIL otherwise + */ + +long auth_gssapi_proxy_valid (void) +{ + char *s,tmp[MAILTMPLEN]; + OM_uint32 smn; + gss_buffer_desc buf; + gss_name_t name; + struct stat sbuf; + sprintf (tmp,"host@%s",mylocalhost ()); + buf.length = strlen (buf.value = tmp) + 1; + memcpy (&auth_gss_proxy_mech,&gss_mech_krb5,sizeof (gss_OID)); + memcpy (&auth_gss_proxy_mech_set,&gss_mech_set_krb5,sizeof (gss_OID_set)); + /* see if can build a name */ + if (gss_import_name (&smn,&buf,gss_nt_service_name,&name) != GSS_S_COMPLETE) + return NIL; /* failed */ + if ((s = strchr (krb5_defkeyname,':')) && stat (++s,&sbuf)) + auth_gss_proxy.server = NIL; /* can't do server if no keytab */ + gss_release_name (&smn,&name);/* finished with name */ + return LONGT; +} + +/* Proxy client authenticator (mihodge) + * Accepts: challenger function + * responder function + * parsed network mailbox structure + * stream argument for functions + * pointer to current trial count + * returned user name + * Returns: T if success, NIL otherwise, number of trials incremented if retry + */ + +#ifndef AUTH_GSS_PROXY_PATH +#define AUTH_GSS_PROXY_PATH "/usr/local/libexec/alpine/bin/wp_gssapi_proxy" +#endif +#define AUTH_GSS_PROXY_MESSAGE 1 +#define AUTH_GSS_PROXY_READ 2 +#define AUTH_GSS_PROXY_SUCCESS 3 +#define AUTH_GSS_PROXY_WRITE 4 +#define AUTH_GSS_PROXY_WRITE_NIL 5 +#define AUTH_GSS_PROXY_EXEC_FAILED 6 + +static unsigned long read_full(int fd,void *buf,unsigned long size) { + unsigned long total,s; + for(total = 0; total < size; total += s) { + s = read(fd,(char*)buf + total,size - total); + if(s == -1) { + if((errno == EAGAIN) || (errno == EINTR)) s = 0; + else return -1; + } else if(s == 0) break; + } + return total; +} + +static unsigned long write_full(int fd,void *buf,unsigned long size) { + unsigned long total,s; + for(total = 0; total < size; total += s) { + s = write(fd,(char*)buf + total,size - total); + if(s == -1) { + if((errno == EAGAIN) || (errno == EINTR)) s = 0; + else return -1; + } + } + return total; +} + +long auth_gssapi_proxy_client (authchallenge_t challenger, + authrespond_t responder, + char *service, + NETMBX *mb,void *stream,unsigned long *trial, + char *user) +{ + char err[MAILTMPLEN]; + int status, ipipe[2], opipe[2]; + unsigned long len,cmd[2],maxlogintrials; + char *buf; + pid_t pid; + long ret = NIL; + + imap_parameters(GET_MAXLOGINTRIALS, (void *) &maxlogintrials); + *trial = maxlogintrials + 1; + err[0] = 0; + strcpy (user,mb->user[0] ? mb->user : myusername ()); + + /* open pipe to gss proxy process */ + if(pipe(ipipe)) { + sprintf (err,"auth_gss_proxy: create pipe error: %s",strerror(errno)); + } else if(pipe(opipe)) { + sprintf (err,"auth_gss_proxy: create pipe error: %s",strerror(errno)); + close(ipipe[0]); + close(ipipe[1]); + } else if((pid = fork()) == -1) { + sprintf (err,"auth_gss_proxy: fork error: %s",strerror(errno)); + close(ipipe[0]); + close(ipipe[1]); + close(opipe[0]); + close(opipe[1]); + + } else if(pid == 0) { /* child process */ + close(ipipe[0]); + close(opipe[1]); + dup2(opipe[0],0); + dup2(ipipe[1],1); + close(opipe[0]); + close(ipipe[1]); + + /* a little overloading of the "err" field here */ + sprintf (err,"%s@%s",service,mb->host); + execlp(AUTH_GSS_PROXY_PATH,AUTH_GSS_PROXY_PATH,err,user,0); + + /* tell parent that exec failed and exit */ + cmd[0] = AUTH_GSS_PROXY_EXEC_FAILED; + cmd[1] = 0; + write_full(1,cmd,sizeof(cmd)); + exit(-1); + + } else { /* parent process */ + close(ipipe[1]); + close(opipe[0]); + + while(len = read_full(ipipe[0],cmd,sizeof(cmd))) { + if (len == -1) { + sprintf (err,"auth_gss_proxy: read error: %s",strerror(errno)); + break; + } else if (len != sizeof(cmd)) { + sprintf (err,"auth_gss_proxy: read error: %lu out of %lu", + len,sizeof(cmd)); + break; + + } else if(cmd[0] == AUTH_GSS_PROXY_EXEC_FAILED) { + sprintf (err,"auth_gss_proxy: could not spawn proxy process"); + break; + + } else if(cmd[0] == AUTH_GSS_PROXY_MESSAGE) { + if(cmd[1]) { + buf = fs_get(cmd[1]); + len = read_full(ipipe[0],buf,cmd[1]); + if(len == -1) { + sprintf (err,"auth_gss_proxy: read error: %s",strerror(errno)); + break; + } else if(len != cmd[1]) { + sprintf (err,"auth_gss_proxy: read error: %lu out of %lu", + len,cmd[1]); + break; + } else { + mm_log(buf,WARN); + } + fs_give ((void **) &buf); + } + + } else if(cmd[0] == AUTH_GSS_PROXY_READ) { + buf = (*challenger) (stream,&len); + if(!buf) len = 0; + if(write_full(opipe[1],&len,sizeof(len)) == -1) { + sprintf (err,"auth_gss_proxy: write error: %s",strerror(errno)); + break; + } else if (buf && (write_full(opipe[1],buf,len) == -1)) { + sprintf (err,"auth_gss_proxy: write error: %s",strerror(errno)); + break; + } + if(buf) fs_give ((void **) &buf); + + } else if(cmd[0] == AUTH_GSS_PROXY_SUCCESS) { + ret = T; + + } else if(cmd[0] == AUTH_GSS_PROXY_WRITE) { + if(cmd[1]) { + buf = fs_get(cmd[1]); + len = read_full(ipipe[0],buf,cmd[1]); + if(len == -1) { + sprintf (err,"auth_gss_proxy: read error: %s",strerror(errno)); + break; + } else if(len != cmd[1]) { + sprintf (err,"auth_gss_proxy: read error: %lu out of %lu", + len,cmd[1]); + break; + } else { + (*responder) (stream,buf,cmd[1]); + } + fs_give ((void **) &buf); + } else { + (*responder) (stream,"",0); + } + + } else if(cmd[0] == AUTH_GSS_PROXY_WRITE_NIL) { + (*responder) (stream,NIL,0); + + } else { + sprintf (err,"auth_gss_proxy: unknown command: %lu",cmd[0]); + break; + } + } + + /* close pipes and wait for process to die */ + close(ipipe[0]); + waitpid(pid,&status,0); + close(opipe[1]); + } + + if(err[0]) mm_log(err,WARN); + return ret; +} + +/* Server authenticator + * Accepts: responder function + * argument count + * argument vector + * Returns: authenticated user name or NIL + */ + +char *auth_gssapi_proxy_server (authresponse_t responder,int argc,char *argv[]) +{ + char *ret = NIL; + char *s,tmp[MAILTMPLEN]; + unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE); + int conf; + OM_uint32 smj,smn,dsmj,dsmn,flags; + OM_uint32 mctx = 0; + gss_name_t crname,name; + gss_OID mech; + gss_buffer_desc chal,resp,buf; + gss_cred_id_t crd; + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + gss_qop_t qop = GSS_C_QOP_DEFAULT; + /* make service name */ + sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL), + tcp_serverhost ()); + buf.length = strlen (buf.value = tmp) + 1; + /* acquire credentials */ + if ((gss_import_name (&smn,&buf,gss_nt_service_name,&crname)) == + GSS_S_COMPLETE) { + if ((smj = gss_acquire_cred (&smn,crname,0,auth_gss_proxy_mech_set,GSS_C_ACCEPT, + &crd,NIL,NIL)) == GSS_S_COMPLETE) { + if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) { + do { /* negotiate authentication */ + smj = gss_accept_sec_context (&smn,&ctx,crd,&resp, + GSS_C_NO_CHANNEL_BINDINGS,&name,&mech, + &chal,&flags,NIL,NIL); + /* don't need response any more */ + fs_give ((void **) &resp.value); + switch (smj) { /* how did it go? */ + case GSS_S_COMPLETE: /* successful */ + /* paranoia */ + if (memcmp (mech->elements,auth_gss_proxy_mech->elements,mech->length)) + fatal ("GSSAPI is bogus"); + case GSS_S_CONTINUE_NEEDED: + if (chal.value) { /* send challenge, get next response */ + resp.value = (*responder) (chal.value,chal.length, + (unsigned long *) &resp.length); + gss_release_buffer (&smn,&chal); + } + break; + } + } + while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED)); + + /* successful exchange? */ + if ((smj == GSS_S_COMPLETE) && + (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) { + /* extract authentication ID from principal */ + if (s = strchr ((char *) buf.value,'@')) *s = '\0'; + /* send security and size */ + memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4); + tmp[0] = AUTH_GSSAPI_P_NONE; + if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){ + resp.value = (*responder) (chal.value,chal.length, + (unsigned long *) &resp.length); + gss_release_buffer (&smn,&chal); + if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) { + /* client request valid */ + if (chal.value && (chal.length > 4) && (chal.length < MAILTMPLEN) + && memcpy (tmp,chal.value,chal.length) && + (tmp[0] & AUTH_GSSAPI_P_NONE)) { + /* tie off authorization ID */ + tmp[chal.length] = '\0'; + if (authserver_login (tmp+4,buf.value,argc,argv) || + authserver_login (lcase (tmp+4),buf.value,argc,argv)) + ret = myusername (); + } + /* done with user name */ + gss_release_buffer (&smn,&chal); + } + /* finished with response */ + fs_give ((void **) &resp.value); + } + /* don't need name buffer any more */ + gss_release_buffer (&smn,&buf); + } + /* don't need client name any more */ + gss_release_name (&smn,&name); + /* don't need context any more */ + if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL); + } + /* finished with credentials */ + gss_release_cred (&smn,&crd); + } + + else { /* can't acquire credentials! */ + if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE) + SERVER_LOG ("Failed to acquire credentials for %s",buf.value); + if (smj != GSS_S_FAILURE) do + switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + mctx = 0; + case GSS_S_CONTINUE_NEEDED: + SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + case GSS_S_CONTINUE_NEEDED: + SERVER_LOG ("GSSAPI mechanism status: %s",resp.value); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + } + /* finished with credentials name */ + gss_release_name (&smn,&crname); + } + return ret; /* return status */ +} |