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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: User.c++,v 1.7 2009/09/29 11:00:37 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 "RE.h"
  29. #include "config.h"
  30. #include <ctype.h>
  31. #include <pwd.h>
  32. #if HAS_CRYPT_H
  33. #include <crypt.h>
  34. #endif
  35. #include <math.h>
  36. #ifndef CHAR_BIT
  37. #ifdef NBBY
  38. #define CHAR_BIT NBBY
  39. #else
  40. #define CHAR_BIT 8
  41. #endif
  42. #endif /* CHAR_BIT */
  43. /*
  44.  * User Access Control Support.
  45.  */
  46. gid_t HylaFAXServer::faxuid = 0; // reserved fax uid
  47. #ifdef HAVE_PAM
  48. extern int
  49. pamconv(int num_msg, STRUCT_PAM_MESSAGE **msg, struct pam_response **resp, void *appdata);
  50. #endif
  51. bool
  52. HylaFAXServer::checkUser(const char* name)
  53. {
  54.     bool check = false;
  55.     FILE* db = fopen(fixPathname(userAccessFile), "r");
  56.     if (db != NULL) {
  57. check = checkuser(db, name) || checkuser(name);
  58. fclose(db);
  59.     } else
  60. logError("Unable to open the user access file %s: %s",
  61.     (const char*) userAccessFile, strerror(errno));
  62.     /*
  63.      * This causes the user to be prompted for a password
  64.      * and then denied access.  We do this to guard against
  65.      * folks that probe the server looking for valid accounts.
  66.      */
  67.     return (true);
  68. }
  69. static bool
  70. nextRecord(FILE* db, char line[], u_int size)
  71. {
  72.     while (fgets(line, size-1, db)) {
  73. char* cp = strchr(line, '#');
  74. if (cp) { // trim trailing white space */
  75.     for (cp = strchr(line, ''); cp > line; cp--)
  76. if (!isspace(cp[-1]))
  77.     break;
  78.     *cp = '';
  79. }
  80. if ((cp = strchr(line, 'n')))
  81.     *cp = '';
  82. if (line[0] != '')
  83.     return (true);
  84.     }
  85.     return (false);
  86. }
  87. bool
  88. HylaFAXServer::checkuser(const char* name)
  89. {
  90. bool retval=false;
  91. #ifdef HAVE_PAM
  92. if (pam_chrooted) {
  93.     logNotice("PAM authentication for %s can't be used for a re-issuance of USER command because of chroot jailn", name);
  94.     return false;
  95. }
  96. /*
  97.  * The effective uid must be privileged enough to
  98.  * handle whatever the PAM module may require.
  99.  */
  100. uid_t ouid = geteuid();
  101. (void) seteuid(0);
  102. pam_handle_t *pamh;
  103. struct pam_conv conv = { pamconv, NULL };
  104. int pamret = pam_start(FAX_SERVICE, name, &conv, &pamh);
  105. if (pamret == PAM_SUCCESS) {
  106.     pamret = pam_set_item(pamh, PAM_RHOST, remoteaddr);
  107.     if (pamret == PAM_SUCCESS) {
  108. pamret = pam_authenticate(pamh, 0);
  109. /* We deliberately ignore PAM_INCOMPLETE here. */
  110. if (pamret == PAM_SUCCESS) {
  111.     pamret = pam_acct_mgmt(pamh, 0);
  112.     if (pamret == PAM_SUCCESS) {
  113. passWd = "";
  114.     } else
  115. logNotice("pam_acct_mgmt failed in checkuser with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  116. } else
  117. #ifdef PAM_INCOMPLETE
  118.     if (pamret != PAM_INCOMPLETE) /* PAM_INCOMPLETE is deliberately anticipated and ignored here. */
  119. #endif
  120. logNotice("pam_authenticate failed in checkuser with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  121.     } else
  122. logNotice("pam_set_item failed in checkuser with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  123. } else
  124.     logNotice("pam_start failed in checkuser with 0x%X: %s", pamret, pam_strerror(pamh, pamret));
  125. if (pamret != PAM_SUCCESS) {
  126.     passWd = "*";
  127.     adminWd = "*";
  128. }
  129. retval = true;
  130. pamEnd(pamh, pamret);
  131. (void) seteuid(ouid);
  132. #endif //HAVE_PAM
  133. return(retval);
  134. }
  135. /*
  136.  * Check the user name and host name/address against
  137.  * the list of users and hosts that are permitted to
  138.  * user the server and setup password handling.
  139.  */
  140. bool
  141. HylaFAXServer::checkuser(FILE* db, const char* name)
  142. {
  143.     struct stat sb;
  144.     if (Sys::fstat(fileno(db), sb) < 0)
  145. return (false);
  146.     if (sb.st_mode&077) { // file must not be publicly readable
  147. logError("Access control file not mode 600; access denied.");
  148. return (false);
  149.     }
  150.     uid = FAXUID_ANON; // anonymous user
  151.     adminWd = "*"; // disallow privileged access
  152.     fxStr dotform  = fxStr::format("%s@%s", name, (const char*) remoteaddr);
  153.     fxStr hostform = fxStr::format("%s@%s", name, (const char*) remotehost);
  154.     rewind(db);
  155.     char line[1024];
  156.     while (nextRecord(db, line, sizeof (line))) {
  157. /*
  158.  * Records are of the form:
  159.  *
  160.  *    [!]regex[:uid[:passwd[:adminwd]]]
  161.  *
  162.  * where regex is a regular expression that must
  163.  * match a string of the form "user@host" or "user@addr"
  164.  * (where addr is the dot-notation form of the client
  165.  * host).  If subsequent fields are present then the
  166.  * first is treated as the numeric ID for the user,
  167.  * followed by the encrypted password that the client
  168.  * must supply.  The next field is the password that
  169.  * must be presented to gain administrative privileges.
  170.  *
  171.  * If the regex is a single word (no @ sign), we take it
  172.  * as a host only short form for (^.*@<input>$)
  173.  *
  174.  * If the first character of the <regex> is a ``!''
  175.  * then the line specifies user(s) to disallow; a match
  176.  * causes the user to be rejected w/o a password prompt.
  177.  * This facility is mainly for backwards compatibility.
  178.  */
  179. char* cp;
  180. bool userandhost = false;
  181. for (cp = line; *cp && *cp != ':'; cp++)
  182.     if (*cp == '\' && *(cp+1)) // skip check of next char unless it's end of string
  183. cp++;
  184.     else if (*cp == '@')
  185. userandhost = true;
  186. const char* base = &line[line[0] == '!'];
  187. fxStr pattern(base, cp-base);
  188. if (! userandhost) {
  189.     pattern.insert("^.*@");
  190.     pattern.append("$");
  191. }
  192. RE pat(pattern);
  193. if (line[0] == '!') { // disallow access on match
  194.     if (pat.Find(dotform) || pat.Find(hostform))
  195. return (false);
  196. } else { // allow access on match
  197.     if (pat.Find(dotform) || pat.Find(hostform)) {
  198. passWd = ""; // no password required
  199. if (*cp == ':') { // :uid[:passwd[:adminwd]]
  200.     if (isdigit(*++cp)) {
  201. uid = atoi(cp);
  202. for (; *cp && *cp != ':'; cp++)
  203.     ;
  204.     }
  205.     if (*cp == ':') { // :passwd[:adminwd]
  206. for (base = ++cp; *cp && *cp != ':'; cp++)
  207.     ;
  208. if (*cp == ':') {
  209.     passWd = fxStr(base, cp-base);
  210.     adminWd = cp+1;
  211. } else
  212.     passWd = base;
  213.     } else
  214. passWd = ""; // no password required
  215. }
  216. return (true);
  217.     }
  218. }
  219.     }
  220.     passWd = "*";
  221.     return (false);
  222. }
  223. fxDECLARE_PtrKeyDictionary(IDCache, u_int, fxStr)
  224. fxIMPLEMENT_PtrKeyObjValueDictionary(IDCache, u_int, fxStr)
  225. /*
  226.  * Read the host access file and fill the ID cache
  227.  * with entries that map fax UID to name.  We pick
  228.  * names by stripping any host part from matching
  229.  * regex's and by mapping ``.*'' user matches to a
  230.  * generic ``anyone'' name.
  231.  *
  232.  * XXX Maybe should convert RE entries to numeric
  233.  *     equivalent of ID to avoid funky names???
  234.  */
  235. void
  236. HylaFAXServer::fillIDCache(void)
  237. {
  238.     idcache = new IDCache;
  239.     FILE* db = fopen(fixPathname(userAccessFile), "r");
  240.     if (db != NULL) {
  241. char line[1024];
  242. while (nextRecord(db, line, sizeof (line))) {
  243.     if (line[0] == '!') // ignore ! entries
  244. continue;
  245.     char* cp;
  246.     for (cp = line; *cp && *cp != ':'; cp++)
  247. ;
  248.     fxStr name(line, cp-line);
  249.     name.resize(name.next(0, '@')); // strip @host part
  250.     if (name == ".*") // map .* -> ``anyone''
  251. name = "anyone";
  252.     if (*cp == ':')
  253. cp++;
  254.     u_int id; // fax UID
  255.     if (isdigit(*cp))
  256. id = atoi(cp);
  257.     else
  258. id = FAXUID_ANON;
  259.     (*idcache)[id] = name;
  260. }
  261. fclose(db);
  262.     }
  263. }
  264. /*
  265.  * Map fax UID to user name.
  266.  */
  267. const char*
  268. HylaFAXServer::userName(u_int id)
  269. {
  270.     if (id == uid) // user currently logged in
  271. return (const char*) the_user;
  272.     if (id == FAXUID_ANON) // anonymous user
  273. return "fax";
  274.     if (idcache == NULL) // load cache from file
  275. fillIDCache();
  276.     const fxStr* hit = idcache->find(id); // check cache
  277.     if (!hit) { // create entry w/ numeric value
  278. (*idcache)[id] = fxStr((int) id, "%u");
  279. hit = idcache->find(id); // new entry
  280.     }
  281.     return (*hit);
  282. }
  283. /*
  284.  * Map user name to fax UID.
  285.  */
  286. bool
  287. HylaFAXServer::userID(const char* name, u_int& id)
  288. {
  289.     if (name == the_user)
  290. id = uid;
  291.     else if (strcmp(name, "fax") == 0)
  292. id = FAXUID_ANON;
  293.     else {
  294. if (idcache == NULL)
  295.     fillIDCache();
  296. for (IDCacheIter iter(*idcache); iter.notDone(); iter++)
  297.     if (iter.value() == name) {
  298. id = iter.key();
  299. return (true);
  300.     }
  301. return (false);
  302.     }
  303.     return (true);
  304. }
  305. static bool
  306. isAllLower(const char* cp)
  307. {
  308.     while (*cp) {
  309. if (!islower(*cp))
  310.     return (false);
  311. cp++;
  312.     }
  313.     return (true);
  314. }
  315. static void
  316. to64(char* cp, long v, int len)
  317. {
  318.     while (--len >= 0) {
  319. *cp++ = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[v&0x3f];
  320. v >>= 6;
  321.     }
  322. }
  323. bool
  324. HylaFAXServer::cvtPasswd(const char* type, const char* pass, fxStr& result)
  325. {
  326.     if (*pass == '') { // null password *IS* permitted
  327. result = "";
  328. return (true);
  329.     }
  330.     if (strlen(pass) <= 5) {
  331. reply(500, "%s password is too short; use 5-8 characters.", type);
  332. return (false);
  333.     }
  334.     if (isAllLower(pass)) {
  335. reply(500, "%s password is all lower-case; use something more.", type);
  336. return (false);
  337.     }
  338.     srandom((int) Sys::now());
  339.     char salt[9];
  340.     /*
  341.      * Contemporary systems use an extended salt that
  342.      * is distinguished by a leading character (``_'').
  343.      * Older systems use a 2-character salt that results
  344.      * in encrypted strings that are easier to crack.
  345.      */
  346. #ifdef _PASSWORD_EFMT1
  347.     salt[0] = _PASSWORD_EFMT1;
  348.     to64(&salt[1], (long)(29 * 25), 4);
  349.     to64(&salt[5], random(), 4);
  350. #else
  351.     to64(&salt[0], random(), 2);
  352. #endif
  353.     result = crypt(pass, salt);
  354.     return (true);
  355. }
  356. #define NBPL (sizeof (u_long) * CHAR_BIT) // bits/u_long
  357. #define SetBit(b) (allocated[(b)/NBPL] |= ((u_long) 1)<<((b)%NBPL))
  358. #define ClrBit(b) (allocated[(b)/NBPL] &= ~(((u_long) 1)<<((b)%NBPL)))
  359. #ifndef howmany
  360. #define howmany(x, y) (((x)+((y)-1))/(y))
  361. #endif
  362. #define N(a) (sizeof (a) / sizeof (a[0]))
  363. bool
  364. HylaFAXServer::findUser(FILE* db, const char* user, u_int& newuid)
  365. {
  366.     rewind(db);
  367.     char line[1024];
  368.     u_long allocated[howmany(FAXUID_MAX+1,NBPL)];
  369.     memset(allocated, 0, sizeof (allocated));
  370.     if (faxuid < FAXUID_MAX)
  371. SetBit(faxuid); // reserved uid
  372.     else
  373. logError("Internal error, "fax" UID (%u) too large.", faxuid);
  374.     SetBit(0); // 0 uid is reserved
  375.     SetBit(FAXUID_ANON); // anonymous uid is reserved
  376.     while (nextRecord(db, line, sizeof (line))) {
  377. if (line[0] == '!')
  378.     continue;
  379. char* cp;
  380. for (cp = line; *cp && *cp != ':'; cp++)
  381.     ;
  382. if (strncmp(user, line, cp-line) == 0)
  383.     return (true);
  384. if (*cp == ':' && isdigit(cp[1])) { // mark uid as in-use
  385.     u_int uid = (u_int) atoi(cp+1);
  386.     if (uid < FAXUID_MAX)
  387. SetBit(uid);
  388.     else
  389. logError("Error in %s:  UID (%u) too large.", (const char*)userAccessFile, uid);
  390. }
  391.     }
  392.     // find unallocated uid
  393.     for (u_int l = 0; l < N(allocated); l++)
  394. if (allocated[l] != (u_long) -1) {
  395.     u_int b = 0;
  396.     for (u_long mask = 1; allocated[l] & mask; mask <<= 1) 
  397. b++;
  398.     newuid = (u_int) (l*NBPL + b);
  399.     return (false);
  400. }
  401.     newuid = (u_int) -1; // no more space
  402.     return (false);
  403. }
  404. bool
  405. HylaFAXServer::addUser(FILE* db, const char* user, u_int uid, const char* upass, const char* apass)
  406. {
  407.     const char* templ = "/" FAX_TMPDIR "/uaddXXXXXX";
  408.     char* buff = strcpy(new char[strlen(templ) + 1], templ);
  409.     int fd = Sys::mkstemp(buff);
  410.     fxStr tfile = buff;
  411.     delete [] buff;
  412.     if (fd < 0) {
  413. reply(550, "Error creating temp file %s: %s.",
  414.     (const char*) tfile, strerror(errno));
  415. return (false);
  416.     }
  417.     rewind(db);
  418.     char buf[8*1024];
  419.     int cc;
  420.     while ((cc = Sys::read(fileno(db), buf, sizeof (buf))) > 0)
  421. if (Sys::write(fd, buf, cc) != cc) {
  422.     perror_reply(550, "Write error", errno);
  423.     Sys::close(fd);
  424.     (void) Sys::unlink(tfile);
  425.     return (false);
  426. }
  427.     fxStr line;
  428.     if (*apass != '')
  429. line = fxStr::format("^%s@:%u:%s:%sn", user, uid, upass, apass);
  430.     else if (*upass != '')
  431. line = fxStr::format("^%s@:%u:%sn", user, uid, upass);
  432.     else
  433. line = fxStr::format("^%s@:%un", user, uid);
  434.     if (Sys::write(fd, line, line.length()) != (ssize_t)line.length()) {
  435. perror_reply(550, "Write error", errno);
  436. Sys::close(fd);
  437. (void) Sys::unlink(tfile);
  438. return (false);
  439.     }
  440.     Sys::close(fd);
  441.     if (Sys::rename(tfile, fixPathname(userAccessFile)) < 0) {
  442. perror_reply(550, "Rename of temp file failed", errno);
  443. (void) Sys::unlink(tfile);
  444. return (false);
  445.     }
  446.     return (true);
  447. }
  448. /*
  449.  * Add a new user to the access control file.
  450.  */
  451. void
  452. HylaFAXServer::addUserCmd(const char* user, const char* up, const char* ap)
  453. {
  454.     logcmd(T_ADDUSER, "%s XXXX YYYY", user);
  455.     fxStr upass, apass;
  456.     if (!cvtPasswd("User", up, upass) || !cvtPasswd("Admin", ap, apass))
  457. return;
  458.     FILE* db = fopen(fixPathname(userAccessFile), "r");
  459.     if (db != NULL) {
  460. u_int newuid;
  461. if (findUser(db, user, newuid))
  462.     reply(500, "User %s is already present.", user);
  463. else if (newuid == (u_int) -1)
  464.     reply(500, "Unable to add user %s; out of user IDs.", user);
  465. else if (addUser(db, user, newuid, upass, apass))
  466.     reply(200, "User %s added with uid %u.", user, newuid);
  467. fclose(db);
  468.     } else
  469. reply(500, "Cannot open user access file %s: %s.",
  470.     (const char*) userAccessFile, strerror(errno));
  471. }
  472. bool
  473. HylaFAXServer::deleteUser(FILE* db, const char* user)
  474. {
  475.     const char* templ = "/" FAX_TMPDIR "/udelXXXXXX";
  476.     char* buff = strcpy(new char[strlen(templ) + 1], templ);
  477.     int fd = Sys::mkstemp(buff);
  478.     fxStr tfile = buff;
  479.     delete [] buff;
  480.     FILE* ftmp;
  481.     if (fd < 0 || (ftmp = fdopen(fd, "w")) == NULL) {
  482.         reply(550, "Error creating temp file %s: %s.",
  483.         (const char*)tfile, strerror(errno));
  484.         return (false);
  485.     }
  486.     /*
  487.      * Scan the existing file for the specified user
  488.      * and copy other entries to the temporary file.
  489.      * Once the entry for the user is found, stop
  490.      * scanning line-by-line and just block-copy the
  491.      * remaining part of the file.
  492.      */
  493.     bool found = false;
  494.     rewind(db);
  495.     char line[8*1024];
  496.     while (fgets(line, sizeof (line)-1, db)) {
  497. if (line[0] != '!') {
  498.     const char* cp;
  499.     for (cp = line; *cp && *cp != 'n' && *cp != ':'; cp++)
  500. ;
  501.     if (strncmp(user, line, cp-line) == 0) {
  502. found = true;
  503. break;
  504.     }
  505. }
  506. fputs(line, ftmp);
  507.     }
  508.     int cc, ignore;
  509.     while ((cc = fread(line, 1, sizeof (line), db)) > 0)
  510. ignore = fwrite(line, cc, 1, ftmp);
  511.     bool ioError = (fclose(ftmp) != 0);
  512.     if (found) {
  513. if (ioError)
  514.     perror_reply(550, "I/O error", errno);
  515. else if (Sys::rename(tfile, fixPathname(userAccessFile)) < 0)
  516.     perror_reply(550, "Rename of temp file failed", errno);
  517. else {
  518.     return (true);
  519.         }
  520.     } else
  521. reply(500, "User %s not found in access file.", user);
  522.     (void) Sys::unlink(tfile);
  523.     return (false);
  524. }
  525. /*
  526.  * Remove a user from the access control file.
  527.  */
  528. void
  529. HylaFAXServer::delUserCmd(const char* user)
  530. {
  531.     logcmd(T_DELUSER, "%s", user);
  532.     FILE* db = fopen(fixPathname(userAccessFile), "r");
  533.     if (db != NULL) {
  534. if (deleteUser(db, user))
  535.     reply(200, "User %s deleted.", user);
  536. fclose(db);
  537.     } else
  538. reply(500, "Cannot open user access file %s: %s.",
  539.     (const char*) userAccessFile, strerror(errno));
  540. }