Login.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:16k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: Login.c++,v 1.12 2008/04/05 19:09:56 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1995-1996 Sam Leffler
  4.  * Copyright (c) 1995-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. #include "HylaFAXServer.h"
  27. #include "Sys.h"
  28. #include <unistd.h>
  29. #include <ctype.h>
  30. #include <fcntl.h>
  31. #include <pwd.h>
  32. #if HAS_CRYPT_H
  33. #include <crypt.h>
  34. #endif
  35. void
  36. HylaFAXServer::loginRefused(const char* why)
  37. {
  38.     if (++loginAttempts >= maxLoginAttempts) {
  39. reply(530, "Login incorrect (closing connection).");
  40. logNotice("Repeated login failures for user %s from %s [%s]"
  41.     , (const char*) the_user
  42.     , (const char*) remotehost
  43.     , (const char*) remoteaddr
  44. );
  45. dologout(0);
  46.     } else {
  47. reply(530, "User %s access denied.", (const char*) the_user);
  48. logNotice("HylaFAX LOGIN REFUSED (%s) FROM %s [%s], %s"
  49.     , why
  50.     , (const char*) remotehost
  51.     , (const char*) remoteaddr
  52.     , (const char*) the_user
  53. );
  54.     }
  55. }
  56. /*
  57.  * USER command. Sets global passwd state if named
  58.  * account exists and is acceptable; sets askpasswd if a
  59.  * PASS command is expected.  If logged in previously,
  60.  * need to reset state.  User account must be accessible
  61.  * from client host according to the contents of the
  62.  * userAccessFile.
  63.  */
  64. void
  65. HylaFAXServer::userCmd(const char* name)
  66. {
  67.     if (IS(LOGGEDIN)) {
  68. if (IS(PRIVILEGED) && the_user == name) {// revert to unprivileged mode
  69.     state &= ~S_PRIVILEGED;
  70.     reply(230, "User %s logged in.", name);
  71.     return;
  72. }
  73.         end_login();
  74.     }
  75.     the_user = name;
  76.     state &= ~S_PRIVILEGED;
  77.     adminWd = "*"; // make sure no admin privileges
  78.     passWd = "*"; // just in case...
  79.     if (checkUser(name)) {
  80. if (passWd != "") {
  81.     state |= S_WAITPASS;
  82.     reply(331, "Password required for %s.", name);
  83.     /*
  84.      * Delay before reading passWd after first failed
  85.      * attempt to slow down password-guessing programs.
  86.      */
  87.     if (loginAttempts)
  88. sleep(loginAttempts);
  89. } else
  90.     login(230);
  91.     } else
  92. loginRefused("user denied");
  93. }
  94. #ifdef HAVE_PAM
  95. int
  96. pamconv(int num_msg, STRUCT_PAM_MESSAGE **msg, struct pam_response **resp, void *appdata)
  97. {
  98. /*
  99.  * This PAM conversation function expects that the PAM modules
  100.  * being used will only have one message.  If this expectation
  101.  * is not met then this will fail, and this will need modification
  102.  * in order to work.
  103.  */
  104. char *password =(char*) appdata;
  105. struct pam_response* replies;
  106. if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
  107.     return PAM_CONV_ERR;
  108. if (password == NULL) {
  109.     /*
  110.      * Solaris doesn't have PAM_CONV_AGAIN defined.
  111.      */
  112.     #ifdef PAM_CONV_AGAIN
  113. return PAM_CONV_AGAIN;
  114.     #else
  115. return PAM_CONV_ERR;
  116.     #endif
  117. }
  118. replies=(struct pam_response*)calloc(num_msg, sizeof(struct pam_response));
  119. replies[0].resp = password;
  120. replies[0].resp_retcode = 0;
  121. *resp = replies;
  122. return (PAM_SUCCESS);
  123. }
  124. #endif //HAVE_PAM
  125. bool
  126. HylaFAXServer::pamIsAdmin(const char* user)
  127. {
  128. bool retval = false;
  129. #ifdef HAVE_PAM
  130. int i;
  131. static struct group* grinfo = getgrnam(admingroup);
  132. const char *curruser = (user == NULL ? the_user.c_str() : user);
  133. if (grinfo != NULL) {
  134. for (i=0; grinfo->gr_mem[i] != NULL; i++) {
  135. if (strcmp(curruser, grinfo->gr_mem[i]) == 0) retval = true;
  136. }
  137. }
  138. #endif //HAVE_PAM
  139. return(retval);
  140. }
  141. /**
  142. * ldapCheck
  143. * function checks if user with selected login and password exists in LDAP
  144. * param user [IN] - pointer to string containing user login
  145. * param pass [IN] - pointer to string containing user password
  146. * return true if user exists
  147. */
  148. bool
  149. HylaFAXServer::ldapCheck(const char* user, const char* pass)
  150. {
  151. bool retval = false;
  152. #ifdef HAVE_LDAP
  153. int err = 0, i = 0;
  154. char* filter = new char[255];
  155. snprintf(filter, 255, "uid=%s", user);
  156. LDAPMessage* pEntries;
  157. LDAPMessage* pEntry;
  158. struct berval **p_arr_values;
  159. struct berval s_UserPasswd;
  160. char* sLDAPUserDN = new char[255];
  161. LDAP* p_LDAPConn;
  162. bool bValidUser = false;
  163. /*
  164.  * See if ldapServerUri has a value.
  165.  * If not, disable using LDAP support.
  166.  */
  167. if (strlen(ldapServerUri) == 0) {
  168. retval = false;
  169. return retval;
  170. }
  171. /*
  172.  * Build the User DN using the LDAP Base value
  173.  * from the config file and the supplied username
  174.  */
  175. strcpy(sLDAPUserDN, "cn=");
  176. strcat(sLDAPUserDN, user);
  177. strcat(sLDAPUserDN, ",");
  178. strcat(sLDAPUserDN, ldapBaseDN);
  179. strcat(sLDAPUserDN, "");
  180. /*
  181.  * Store the password in the berval struct for ldap_sasl_bind_s
  182.  */
  183. s_UserPasswd.bv_val = (char *)pass;
  184. s_UserPasswd.bv_len = strlen(pass);
  185. /*
  186.  * Connect to the LDAP server and set the version
  187.  */
  188. ldap_initialize(&p_LDAPConn, ldapServerUri);
  189. if (p_LDAPConn == NULL)
  190. {
  191. reply(530, "Unable to connect to LDAP");
  192. return false;
  193. }
  194. err = ldap_set_option(p_LDAPConn, LDAP_OPT_PROTOCOL_VERSION, (void *) &ldapVersion);
  195. if (err != LDAP_SUCCESS)
  196. {
  197. reply(530, "Set Option LDAP error %d: %s", err, ldap_err2string(err));
  198. ldap_unbind_ext_s(p_LDAPConn, NULL, NULL);
  199. return false;
  200. }
  201. /*
  202.  * Attempt a simple bind to the LDAP server
  203.  * using the user credentials given to us.
  204.  *
  205.  * If we fail, then the provided credentials
  206.  * are incorrect
  207.  */
  208. err = ldap_sasl_bind_s(p_LDAPConn, sLDAPUserDN, LDAP_SASL_SIMPLE, &s_UserPasswd, NULL, NULL, NULL);
  209. if (err != LDAP_SUCCESS)
  210. {
  211. reply(530, "Bind LDAP error %d: %s", err, ldap_err2string(err));
  212. ldap_unbind_ext_s(p_LDAPConn, NULL, NULL);
  213. return false;
  214. /*
  215.  * Search the LDAP tree for the group that
  216.  * a user needs to be in to have fax server
  217.  * access.
  218.  */
  219. err = ldap_search_ext_s(p_LDAPConn, sLDAPUserDN, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, NULL, 0, &pEntries);
  220. if (err != LDAP_SUCCESS)
  221. {
  222. reply(530, "Search LDAP error %d: %s", err, ldap_err2string(err));
  223. ldap_unbind_ext_s(p_LDAPConn, NULL, NULL);
  224. return false;
  225. }
  226. /*
  227.  * Get the first entry, which should be
  228.  * our desired user
  229.  */
  230. pEntry = ldap_first_entry(p_LDAPConn, pEntries);
  231. if (pEntry == NULL)
  232. {
  233. reply(530, "LDAP user not found");
  234. ldap_unbind_ext_s(p_LDAPConn, NULL, NULL);
  235. return false;
  236. }
  237. /*
  238.  * Fetch all of the groupMembership values
  239.  */
  240. p_arr_values = ldap_get_values_len(p_LDAPConn, pEntry, "groupMembership");
  241. if (p_arr_values == NULL)
  242. {
  243. reply(530, "LDAP attribute groupMembership not found");
  244. ldap_value_free_len(p_arr_values);
  245. ldap_unbind_ext_s(p_LDAPConn, NULL, NULL);
  246. return false;
  247. /*
  248.  * Check each value to see if it matches
  249.  * our desired value specifed in the config
  250.  */
  251. while (p_arr_values[i] != NULL)
  252. {
  253. if (strcmp(ldapReqGroup, p_arr_values[i]->bv_val) == 0)
  254. {
  255. bValidUser = true;
  256. break;
  257. }
  258. i++;
  259. }
  260. if (bValidUser)
  261. retval = true;
  262. else
  263. {
  264. reply(530, "Access Denied");
  265. ldap_value_free_len(p_arr_values);
  266. ldap_unbind_ext_s(p_LDAPConn, NULL, NULL);
  267. return false;
  268. }
  269. ldap_value_free_len(p_arr_values);
  270. ldap_unbind_ext_s(p_LDAPConn, NULL, NULL);
  271. #endif // HAVE_LDAP
  272. return retval;
  273. }
  274. bool
  275. HylaFAXServer::pamCheck(const char* user, const char* pass)
  276. {
  277. bool retval = false;
  278. #ifdef HAVE_PAM
  279. if (user == NULL) user = the_user;
  280. if (pass == NULL) pass = passWd.c_str();
  281. /*
  282.  * The effective uid must be privileged enough to
  283.  * handle whatever the PAM module may require.
  284.  */
  285. uid_t ouid = geteuid();
  286. (void) seteuid(0);
  287. pam_handle_t *pamh;
  288. struct pam_conv conv = {pamconv, NULL};
  289. conv.appdata_ptr = strdup(pass);
  290. int pamret = pam_start(FAX_SERVICE, user, &conv, &pamh);
  291. if (pamret == PAM_SUCCESS) {
  292.     pamret = pam_set_item(pamh, PAM_RHOST, remoteaddr);
  293.     if (pamret == PAM_SUCCESS) {
  294. pamret = pam_set_item(pamh, PAM_CONV, (const void*)&conv);
  295. if (pamret == PAM_SUCCESS) {
  296. #ifdef PAM_INCOMPLETE
  297.     u_int tries = 10;
  298.     do {
  299. /*
  300.  * PAM supports event-driven applications by returning PAM_INCOMPLETE
  301.  * and requiring the application to recall pam_authenticate after the
  302.  * underlying PAM module is ready.  The application is supposed to
  303.  * utilize the pam_conv structure to determine when the authentication
  304.  * module is ready.  However, in our case we're not event-driven, and
  305.  * so we can wait, and a call to sleep saves us the headache.
  306.  */
  307. #endif // PAM_INCOMPLETE
  308. pamret = pam_authenticate(pamh, 0);
  309. #ifdef PAM_INCOMPLETE
  310.     } while (pamret == PAM_INCOMPLETE && --tries && !sleep(1));
  311. #endif // PAM_INCOMPLETE
  312.     if (pamret == PAM_SUCCESS) {
  313. pamret = pam_acct_mgmt(pamh, 0);
  314. if (pamret == PAM_SUCCESS) {
  315.     retval = true;
  316. } else
  317.     logNotice("pam_acct_mgmt failed in pamCheck with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  318.     } else
  319. logNotice("pam_authenticate failed in pamCheck with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  320. } else
  321.     logNotice("pam_set_item (PAM_CONV) failed in pamCheck with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  322.     } else
  323. logNotice("pam_set_item (PAM_RHOST) failed in pamCheck with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  324. } else
  325.     logNotice("pam_start failed in pamCheck with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  326. pamEnd(pamh, pamret);
  327. (void) seteuid(ouid);
  328. #endif
  329. return retval;
  330. }
  331. #ifdef HAVE_PAM
  332. void HylaFAXServer::pamEnd(pam_handle_t *pamh, int pamret)
  333. {
  334.     if (pamret == PAM_SUCCESS)
  335.     {
  336. if (pamIsAdmin())
  337.     state |= S_PRIVILEGED;
  338. char *newname=NULL;
  339. /*
  340.  * Solaris has proprietary pam_[sg]et_item() extension.
  341.  * Sun defines PAM_MSG_VERSION therefore is possible to use
  342.  * it in order to recognize the extensions of Solaris
  343.  */
  344. #ifdef PAM_MSG_VERSION
  345.     pamret = pam_get_item(pamh, PAM_USER, (void **)&newname);
  346. #else
  347.     pamret = pam_get_item(pamh, PAM_USER, (const void **)&newname);
  348. #endif
  349. if (pamret == PAM_SUCCESS && newname != NULL)
  350.     the_user = strdup(newname);
  351. struct passwd* uinfo=getpwnam((const char *)the_user);
  352. if (uinfo != NULL) {
  353.     uid = uinfo->pw_uid;
  354. }
  355.     }
  356.     pamret = pam_end(pamh, pamret);
  357.     pamh = NULL;
  358. }
  359. #endif //HAVE_PAM
  360. void
  361. HylaFAXServer::passCmd(const char* pass)
  362. {
  363.     if (IS(LOGGEDIN)) {
  364.         reply(503, "Already logged in as USER %s.", (const char*) the_user);
  365.         return;
  366.     }
  367.     if (!IS(WAITPASS)) {
  368.         reply(503, "Login with USER first.");
  369.         return;
  370.     }
  371.     state &= ~S_WAITPASS;
  372.     /*
  373.      * Disable long reply messages for old (broken) FTP
  374.      * clients if the first character of the password
  375.      * is a ``-''.
  376.      */
  377.     if (pass[0] == '-') {
  378. state &= ~S_LREPLIES;
  379. pass++;
  380.     } else
  381. state |= S_LREPLIES;
  382.     /*
  383.      * Check hosts.hfaxd first, then PAM, and last, LDAP
  384.      */
  385.     if (pass[0] == '' || !(strcmp(crypt(pass, passWd), passWd) == 0 || 
  386.      pamCheck(the_user, pass) || 
  387.      ldapCheck(the_user,pass)))
  388.     {
  389. if (++loginAttempts >= maxLoginAttempts) {
  390.     reply(530, "Login incorrect (closing connection).");
  391.     logNotice("Repeated login failures for user %s from %s [%s]"
  392. , (const char*) the_user
  393. , (const char*) remotehost
  394. , (const char*) remoteaddr
  395.     );
  396.     dologout(0);
  397. }
  398. reply(530, "Login incorrect.");
  399. logInfo("Login failed from %s [%s], %s"
  400.     , (const char*) remotehost
  401.     , (const char*) remoteaddr
  402.     , (const char*) the_user
  403. );
  404. return;
  405.     }
  406.     login(230);
  407. }
  408. /*
  409.  * Login is shared between SNPP and HylaFAX RFC 959 protocol,
  410.  * but different protocols use different codes for success.
  411.  * We make the caller tell us what success is.
  412.  */
  413. void
  414. HylaFAXServer::login(int code)
  415. {
  416.     loginAttempts = 0; // this time successful
  417.     state |= S_LOGGEDIN;
  418.     uid_t ouid = geteuid();
  419.     (void) seteuid(0);
  420.     bool isSetup = (chroot(".") >= 0 && chdir("/") >= 0);
  421.     /*
  422.      * Install the client's fax-uid as the effective gid
  423.      * so that created files automatically are given the
  424.      * correct ``ownership'' (we store the owner's fax-uid
  425.      * in the group-id field of the inode).
  426.      */
  427.     if (isSetup)
  428. (void) setegid(uid);
  429.     (void) seteuid(ouid);
  430.     if (!isSetup) {
  431. reply(550, "Cannot set privileges.");
  432. end_login();
  433. return;
  434.     }
  435. #ifdef HAVE_PAM
  436.     pam_chrooted = true;
  437. #endif
  438.     (void) isShutdown(false); // display any shutdown messages
  439.     reply(code, "User %s logged in.", (const char*) the_user);
  440.     if (TRACE(LOGIN))
  441. logInfo("FAX LOGIN FROM %s [%s], %s"
  442.     , (const char*) remotehost
  443.     , (const char*) remoteaddr
  444.     , (const char*) the_user
  445. );
  446.     (void) umask(077);
  447.     if (tracingLevel & (TRACE_INXFERS|TRACE_OUTXFERS))
  448.         xferfaxlog = Sys::open(xferfaxLogFile, O_WRONLY|O_APPEND|O_CREAT, 0600);
  449.     initDefaultJob(); // setup connection-related state
  450.     dirSetup(); // initialize directory handling
  451. if (pamIsAdmin()) state |= S_PRIVILEGED;
  452. }
  453. void
  454. HylaFAXServer::adminCmd(const char* pass)
  455. {
  456.     fxAssert(IS(LOGGEDIN), "ADMIN command permitted when not logged in");
  457.     // NB: null adminWd is permitted
  458.     if ((strcmp(crypt(pass, adminWd), adminWd) != 0) && !pamIsAdmin()) {
  459. if (++adminAttempts >= maxAdminAttempts) {
  460.     reply(530, "Password incorrect (closing connection).");
  461.     logNotice("Repeated admin failures from %s [%s]"
  462. , (const char*) remotehost
  463. , (const char*) remoteaddr
  464.     );
  465.     dologout(0);
  466. } else {
  467.     reply(530, "Password incorrect.");
  468.     logInfo("ADMIN failed from %s [%s], %s"
  469. , (const char*) remotehost
  470. , (const char*) remoteaddr
  471. , (const char*) the_user
  472.     );
  473. }
  474. return;
  475.     }
  476.     if (TRACE(SERVER))
  477. logInfo("FAX ADMIN FROM %s [%s], %s"
  478.     , (const char*) remotehost
  479.     , (const char*) remoteaddr
  480.     , (const char*) the_user
  481. );
  482.     adminAttempts = 0;
  483.     state |= S_PRIVILEGED;
  484.     reply(230, "Administrative privileges established.");
  485. }
  486. /*
  487.  * Terminate login as previous user, if any,
  488.  * resetting state; used when USER command is
  489.  * given or login fails.
  490.  */
  491. void
  492. HylaFAXServer::end_login(void)
  493. {
  494.     if (IS(LOGGEDIN)) {
  495. uid_t ouid = geteuid();
  496. seteuid(0);
  497. seteuid(ouid);
  498.     }
  499.     state &= ~(S_LOGGEDIN|S_PRIVILEGED|S_WAITPASS);
  500.     passWd = "*";
  501.     adminWd = "*";
  502. }
  503. /*
  504.  * Record logout in wtmp file, cleanup state,
  505.  * and exit with supplied status.
  506.  */
  507. void
  508. HylaFAXServer::dologout(int status)
  509. {
  510.     if (IS(LOGGEDIN))
  511. end_login();
  512.     if (trigSpec != "") {
  513. fxStr emsg;
  514. cancelTrigger(emsg);
  515.     }
  516.     for (u_int i = 0, n = tempFiles.length(); i < n; i++)
  517. (void) Sys::unlink(tempFiles[i]);
  518.     if (xferfaxlog != -1)
  519.         Sys::close(xferfaxlog);
  520.     if (clientFd != -1)
  521. Sys::close(clientFd);
  522.     if (clientFIFOName != "") {
  523.       /* we need to check for the FIFO since dologout() might be called
  524.        * before we are chroot()ed... *sigh*
  525.        */
  526.       if (Sys::isFIFOFile( "/" | clientFIFOName)) {
  527.           Sys::unlink("/" | clientFIFOName);
  528.       } else if (Sys::isFIFOFile( FAX_SPOOLDIR "/" | clientFIFOName)) {
  529.           Sys::unlink( FAX_SPOOLDIR "/" | clientFIFOName);
  530.       }
  531.     }
  532.     for (JobDictIter iter(blankJobs); iter.notDone(); iter++) {
  533. Job* job = iter.value();
  534. fxStr file("/" | job->qfile);
  535. Sys::unlink(file);
  536.     }
  537.     _exit(status); // beware of flushing buffers after a SIGPIPE
  538. }