WvStreams
wvpam.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2003 Net Integration Technologies, Inc.
00004  *
00005  * A WvStream that authenticates with PAM before allowing any reading or
00006  * writing.  See wvpam.h.
00007  */
00008 #include "wvlog.h"
00009 #include "wvpam.h"
00010 #include "wvautoconf.h"
00011 
00012 // If PAM not installed at compile time, stub this out
00013 #ifndef HAVE_SECURITY_PAM_APPL_H
00014 
00015 WvPam::WvPam(WvStringParm _appname)
00016     : log("PAM Auth", WvLog::Info), appname(_appname)
00017 {
00018     err.seterr("Compiled without PAM Support!\n");
00019 }
00020          
00021           
00022 WvPam::WvPam(WvStringParm _appname, WvStringParm rhost,
00023              WvStringParm user, WvStringParm password) 
00024     : log("PAM Auth", WvLog::Info), appname(_appname)  
00025 {
00026     err.seterr("Compiled without PAM Support!\n");
00027 }
00028 
00029 
00030 WvPam::~WvPam()
00031 {
00032 }
00033 
00034 bool WvPam::authenticate(WvStringParm rhost, WvStringParm user, WvStringParm password)
00035 {
00036     return false;
00037 }
00038 
00039 WvString WvPam::getuser() const
00040 {
00041     return WvString::null;
00042 }
00043  
00044  
00045 void WvPam::getgroups(WvStringList &l) const
00046 {
00047 }
00048 
00049 #else   // HAVE_SECURITY_PAM_APPL_H
00050 
00051 #include <security/pam_appl.h>
00052 #include <sys/types.h>
00053 #include <pwd.h>
00054 #include <grp.h>
00055 
00056 #include "wvaddr.h"
00057 
00058 
00059 class WvPamData
00060 {
00061 public:
00062     pam_handle_t *pamh;
00063     int status;
00064     WvString failmsg, user;
00065     WvStringList groups;
00066 
00067     WvPamData()
00068         : pamh(NULL), status(PAM_SUCCESS), user("")
00069         { }
00070     
00071     WvPamData(WvStringParm _failmsg)
00072         : pamh(NULL), status(PAM_SUCCESS), failmsg(_failmsg)
00073         { }
00074 };
00075 
00076 
00078 #if HAVE_BROKEN_PAM
00079 int noconv(int num_msg, struct pam_message **msgm,
00080         struct pam_response **response, void *userdata)
00081 #else
00082 int noconv(int num_msg, const struct pam_message **msgm,
00083         struct pam_response **response, void *userdata)
00084 #endif
00085 {
00086     // if you need to ask things, it won't work
00087     return PAM_CONV_ERR;
00088 }
00089 
00090 
00091 // The password gets passed in from userdata, and we simply echo it back
00092 // out in the response... *sigh* This is because pam expects this function
00093 // to actually interact with the user, and get their password.
00094 #if HAVE_BROKEN_PAM
00095 static int passconv(int num_msg, struct pam_message **msgm,
00096         struct pam_response **response, void *userdata)
00097 #else
00098 static int passconv(int num_msg, const struct pam_message **msgm,
00099         struct pam_response **response, void *userdata)
00100 #endif
00101 {
00102     struct pam_response *password_echo;
00103     
00104     password_echo = (struct pam_response *)calloc(num_msg,
00105                                                   sizeof(struct pam_response));
00106     password_echo->resp = (char *)userdata;
00107     password_echo->resp_retcode = 0;
00108     
00109     *response = password_echo;
00110     
00111     return PAM_SUCCESS;
00112 }
00113 
00114 WvPam::WvPam(WvStringParm _appname)
00115     : log("PAM Auth", WvLog::Info), appname(_appname)
00116 {
00117     init();
00118 }
00119  
00120 
00121 WvPam::WvPam(WvStringParm _appname, WvStringParm rhost,
00122              WvStringParm user, WvStringParm password)
00123     : log("PAM Auth", WvLog::Info), appname(_appname)
00124 {
00125     if (init())
00126         authenticate(rhost, user, password);
00127 }
00128 
00129 WvPam::~WvPam()
00130 {
00131     log(WvLog::Debug2, "Shutting down PAM Session for: %s\n", appname);
00132     if (d->status == PAM_SUCCESS)
00133         pam_close_session(d->pamh, 0);
00134     pam_end(d->pamh, d->status);
00135     d->groups.zap();
00136     delete d;       
00137 }
00138  
00139  
00140 bool WvPam::init()
00141 {
00142     d = new WvPamData();
00143     log(WvLog::Debug2, "Starting up PAM Session for: %s\n", appname);
00144     err.seterr("Not yet authenticated...");
00145     
00146     struct pam_conv c;
00147     c.conv = noconv;  
00148     c.appdata_ptr = NULL;
00149 
00150     d->pamh = NULL;
00151     d->status = pam_start(appname, d->user, &c, &d->pamh);
00152     if (check_pam_status("pam_start")) return true;
00153     return false;
00154 }
00155 
00156 bool WvPam::authenticate(WvStringParm rhost, WvStringParm user, WvStringParm password)
00157 {
00158     // Just in case...
00159     assert(d);
00160     
00161     if (!!rhost)
00162     {
00163         d->status = pam_set_item(d->pamh, PAM_RHOST, rhost);
00164         if (!check_pam_status("rhost setup")) 
00165             return false; 
00166     }
00167 
00168     if (!!user)
00169     {
00170         d->user = user;
00171         d->status = pam_set_item(d->pamh, PAM_USER, user);
00172         if (!check_pam_status("user setup")) 
00173             return false;
00174     }
00175 
00176     if (!!password)
00177     {
00178         struct pam_conv c;
00179         c.conv = passconv;
00180         c.appdata_ptr = strdup(password);
00181         d->status = pam_set_item(d->pamh, PAM_CONV, &c);
00182         if (!check_pam_status("conversation setup")) 
00183             return false;
00184         
00185         d->status = pam_set_item(d->pamh, PAM_AUTHTOK, password);
00186         if (!check_pam_status("password setup")) 
00187             return false;   
00188     }
00189 
00190 #if HAVE_BROKEN_PAM
00191     void *x = NULL;
00192 #else
00193     const void *x = NULL;
00194 #endif
00195     d->status = pam_get_item(d->pamh, PAM_USER, &x);
00196     if (!check_pam_status("get username")) 
00197         return false;
00198     d->user = (const char *)x;
00199     d->user.unique();
00200 
00201     log("Starting Authentication for %s@%s\n", d->user, rhost);
00202     
00203     d->status = pam_authenticate(d->pamh, PAM_DISALLOW_NULL_AUTHTOK | PAM_SILENT);
00204     if (!check_pam_status("authentication")) return false;
00205 
00206     d->status = pam_acct_mgmt(d->pamh, PAM_DISALLOW_NULL_AUTHTOK | PAM_SILENT);
00207     if (!check_pam_status("account management")) return false;
00208     
00209     d->status = pam_setcred(d->pamh, PAM_ESTABLISH_CRED);
00210     if (!check_pam_status("credentials")) return false;  
00211 
00212     d->status = pam_open_session(d->pamh, 0);
00213     if (!check_pam_status("session open")) return false;
00214 
00215     // Grab the current user name (now that we've authenticated)
00216     if (!d->user)
00217     {
00218         const void *x = NULL;
00219         d->status = pam_get_item(d->pamh, PAM_USER, &x);
00220         if (!check_pam_status("get username")) return false;
00221         d->user = (const char *)x;
00222         d->user.unique();
00223     }
00224     log("Session open as user '%s'\n", d->user);
00225     
00226     // If we made it here, we're clear of everything, and we can go
00227     // to a no error status.
00228     err.noerr();
00229     
00230     return true;
00231 }
00232 
00233 
00234 bool WvPam::check_pam_status(WvStringParm s)
00235 {
00236     if (d->status == PAM_SUCCESS)
00237     {
00238         log(WvLog::Debug2, "PAM %s succeeded.\n", s);
00239         return true;
00240     }
00241     else
00242     {   
00243         WvString msg("PAM %s failed: %s\n", s, pam_strerror(d->pamh, d->status));
00244         log(WvLog::Info, msg);
00245         err.seterr(msg);
00246         d->user = WvString::null;
00247         d->groups.zap();
00248         return false;   
00249     }
00250 }
00251  
00252  
00253 WvString WvPam::getuser() const
00254 {
00255     return d->user;
00256 }
00257  
00258  
00259 void WvPam::getgroups(WvStringList &l) const
00260 {
00261     assert(l.isempty());
00262 
00263     // Cache after the first time...
00264     if (d->groups.isempty())
00265     {
00266         setgrent();
00267         struct group *gr;
00268         while ((gr = getgrent()))
00269         {
00270             for (char **i = gr->gr_mem; *i != NULL; i++)
00271             {
00272                 if (strcmp(*i, d->user) == 0)
00273                 {
00274                     d->groups.append(new WvString(gr->gr_name), true);
00275                     break;
00276                 }
00277             }
00278         }    
00279         endgrent();
00280     }
00281      
00282     WvStringList::Iter i(d->groups);
00283     for (i.rewind(); i.next(); )
00284         l.append(new WvString(*i), true);
00285 }
00286 
00287 
00288 
00289 
00290 
00291 
00292 #endif // HAVE_SECURITY_PAM_APPL_H