(linenum→info "unix/slp.c:2238")

glibc/2.7/nscd/pwdcache.c

    1: /* Cache handling for passwd lookup.
    2:    Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc.
    3:    This file is part of the GNU C Library.
    4:    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
    5: 
    6:    This program is free software; you can redistribute it and/or modify
    7:    it under the terms of the GNU General Public License as published
    8:    by the Free Software Foundation; version 2 of the License, or
    9:    (at your option) any later version.
   10: 
   11:    This program is distributed in the hope that it will be useful,
   12:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14:    GNU General Public License for more details.
   15: 
   16:    You should have received a copy of the GNU General Public License
   17:    along with this program; if not, write to the Free Software Foundation,
   18:    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
   19: 
   20: #include <alloca.h>
   21: #include <assert.h>
   22: #include <errno.h>
   23: #include <error.h>
   24: #include <libintl.h>
   25: #include <pwd.h>
   26: #include <stdbool.h>
   27: #include <stddef.h>
   28: #include <stdio.h>
   29: #include <stdlib.h>
   30: #include <string.h>
   31: #include <time.h>
   32: #include <unistd.h>
   33: #include <sys/mman.h>
   34: #include <sys/socket.h>
   35: #include <stackinfo.h>
   36: 
   37: #include "nscd.h"
   38: #include "dbg_log.h"
   39: #ifdef HAVE_SENDFILE
   40: # include <kernel-features.h>
   41: #endif
   42: 
   43: /* This is the standard reply in case the service is disabled.  */
   44: static const pw_response_header disabled =
   45: {
   46:   .version = NSCD_VERSION,
   47:   .found = -1,
   48:   .pw_name_len = 0,
   49:   .pw_passwd_len = 0,
   50:   .pw_uid = -1,
   51:   .pw_gid = -1,
   52:   .pw_gecos_len = 0,
   53:   .pw_dir_len = 0,
   54:   .pw_shell_len = 0
   55: };
   56: 
   57: /* This is the struct describing how to write this record.  */
   58: const struct iovec pwd_iov_disabled =
   59: {
   60:   .iov_base = (void *) &disabled,
   61:   .iov_len = sizeof (disabled)
   62: };
   63: 
   64: 
   65: /* This is the standard reply in case we haven't found the dataset.  */
   66: static const pw_response_header notfound =
   67: {
   68:   .version = NSCD_VERSION,
   69:   .found = 0,
   70:   .pw_name_len = 0,
   71:   .pw_passwd_len = 0,
   72:   .pw_uid = -1,
   73:   .pw_gid = -1,
   74:   .pw_gecos_len = 0,
   75:   .pw_dir_len = 0,
   76:   .pw_shell_len = 0
   77: };
   78: 
   79: 
   80: static void
   81: cache_addpw (struct database_dyn *db, int fd, request_header *req,
   82:              const void *key, struct passwd *pwd, uid_t owner,
   83:              struct hashentry *he, struct datahead *dh, int errval)
   84: {
   85:   ssize_t total;
   86:   ssize_t written;
   87:   time_t t = time (NULL);
   88: 
   89:   /* We allocate all data in one memory block: the iov vector,
   90:      the response header and the dataset itself.  */
   91:   struct dataset
   92:   {
   93:     struct datahead head;
   94:     pw_response_header resp;
   95:     char strdata[0];
   96:   } *dataset;
   97: 
   98:   assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
   99: 
  100:   if (pwd == NULL)
  101:     {
  102:       if (he != NULL && errval == EAGAIN)
  103:         {
  104:           /* If we have an old record available but cannot find one
  105:              now because the service is not available we keep the old
  106:              record and make sure it does not get removed.  */
  107:           if (reload_count != UINT_MAX && dh->nreloads == reload_count)
  108:             /* Do not reset the value if we never not reload the record.  */
  109:             dh->nreloads = reload_count - 1;
  110: 
  111:           written = total = 0;
  112:         }
  113:       else
  114:         {
  115:           /* We have no data.  This means we send the standard reply for this
  116:              case.  */
  117:           written = total = sizeof (notfound);
  118: 
  119:           if (fd != -1)
  120:             written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
  121:                                                 MSG_NOSIGNAL));
  122: 
  123:           dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
  124:           /* If we cannot permanently store the result, so be it.  */
  125:           if (dataset != NULL)
  126:             {
  127:               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
  128:               dataset->head.recsize = total;
  129:               dataset->head.notfound = true;
  130:               dataset->head.nreloads = 0;
  131:               dataset->head.usable = true;
  132: 
  133:               /* Compute the timeout time.  */
  134:               dataset->head.timeout = t + db->negtimeout;
  135: 
  136:               /* This is the reply.  */
  137:               memcpy (&dataset->resp, &notfound, total);
  138: 
  139:               /* Copy the key data.  */
  140:               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
  141: 
  142:               /* If necessary, we also propagate the data to disk.  */
  143:               if (db->persistent)
  144:                 {
  145:                   // XXX async OK?
  146:                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
  147:                   msync ((void *) pval,
  148:                          ((uintptr_t) dataset & pagesize_m1)
  149:                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
  150:                 }
  151: 
  152:               /* Now get the lock to safely insert the records.  */
  153:               pthread_rwlock_rdlock (&db->lock);
  154: 
  155:               if (cache_add (req->type, key_copy, req->key_len,
  156:                              &dataset->head, true, db, owner) < 0)
  157:                 /* Ensure the data can be recovered.  */
  158:                 dataset->head.usable = false;
  159: 
  160: 
  161:               pthread_rwlock_unlock (&db->lock);
  162: 
  163:               /* Mark the old entry as obsolete.  */
  164:               if (dh != NULL)
  165:                 dh->usable = false;
  166:             }
  167:           else
  168:             ++db->head->addfailed;
  169:         }
  170:     }
  171:   else
  172:     {
  173:       /* Determine the I/O structure.  */
  174:       size_t pw_name_len = strlen (pwd->pw_name) + 1;
  175:       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
  176:       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
  177:       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
  178:       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
  179:       char *cp;
  180:       const size_t key_len = strlen (key);
  181:       const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
  182:       char *buf = alloca (buf_len);
  183:       ssize_t n;
  184: 
  185:       /* We need this to insert the `byuid' entry.  */
  186:       int key_offset;
  187:       n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
  188:                     &key_offset, (char *) key) + 1;
  189: 
  190:       written = total = (sizeof (struct dataset) + pw_name_len + pw_passwd_len
  191:                          + pw_gecos_len + pw_dir_len + pw_shell_len);
  192: 
  193:       /* If we refill the cache, first assume the reconrd did not
  194:          change.  Allocate memory on the cache since it is likely
  195:          discarded anyway.  If it turns out to be necessary to have a
  196:          new record we can still allocate real memory.  */
  197:       bool alloca_used = false;
  198:       dataset = NULL;
  199: 
  200:       if (he == NULL)
  201:         {
  202:           dataset = (struct dataset *) mempool_alloc (db, total + n);
  203:           if (dataset == NULL)
  204:             ++db->head->addfailed;
  205:         }
  206: 
  207:       if (dataset == NULL)
  208:         {
  209:           /* We cannot permanently add the result in the moment.  But
  210:              we can provide the result as is.  Store the data in some
  211:              temporary memory.  */
  212:           dataset = (struct dataset *) alloca (total + n);
  213: 
  214:           /* We cannot add this record to the permanent database.  */
  215:           alloca_used = true;
  216:         }
  217: 
  218:       dataset->head.allocsize = total + n;
  219:       dataset->head.recsize = total - offsetof (struct dataset, resp);
  220:       dataset->head.notfound = false;
  221:       dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
  222:       dataset->head.usable = true;
  223: 
  224:       /* Compute the timeout time.  */
  225:       dataset->head.timeout = t + db->postimeout;
  226: 
  227:       dataset->resp.version = NSCD_VERSION;
  228:       dataset->resp.found = 1;
  229:       dataset->resp.pw_name_len = pw_name_len;
  230:       dataset->resp.pw_passwd_len = pw_passwd_len;
  231:       dataset->resp.pw_uid = pwd->pw_uid;
  232:       dataset->resp.pw_gid = pwd->pw_gid;
  233:       dataset->resp.pw_gecos_len = pw_gecos_len;
  234:       dataset->resp.pw_dir_len = pw_dir_len;
  235:       dataset->resp.pw_shell_len = pw_shell_len;
  236: 
  237:       cp = dataset->strdata;
  238: 
  239:       /* Copy the strings over into the buffer.  */
  240:       cp = mempcpy (cp, pwd->pw_name, pw_name_len);
  241:       cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
  242:       cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
  243:       cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
  244:       cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
  245: 
  246:       /* Finally the stringified UID value.  */
  247:       memcpy (cp, buf, n);
  248:       char *key_copy = cp + key_offset;
  249:       assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
  250: 
  251:       /* Now we can determine whether on refill we have to create a new
  252:          record or not.  */
  253:       if (he != NULL)
  254:         {
  255:           assert (fd == -1);
  256: 
  257:           if (total + n == dh->allocsize
  258:               && total - offsetof (struct dataset, resp) == dh->recsize
  259:               && memcmp (&dataset->resp, dh->data,
  260:                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
  261:             {
  262:               /* The data has not changed.  We will just bump the
  263:                  timeout value.  Note that the new record has been
  264:                  allocated on the stack and need not be freed.  */
  265:               dh->timeout = dataset->head.timeout;
  266:               ++dh->nreloads;
  267:             }
  268:           else
  269:             {
  270:               /* We have to create a new record.  Just allocate
  271:                  appropriate memory and copy it.  */
  272:               struct dataset *newp
  273:                 = (struct dataset *) mempool_alloc (db, total + n);
  274:               if (newp != NULL)
  275:                 {
  276:                   /* Adjust pointer into the memory block.  */
  277:                   cp = (char *) newp + (cp - (char *) dataset);
  278:                   key_copy = (char *) newp + (key_copy - (char *) dataset);
  279: 
  280:                   dataset = memcpy (newp, dataset, total + n);
  281:                   alloca_used = false;
  282:                 }
  283: 
  284:               /* Mark the old record as obsolete.  */
  285:               dh->usable = false;
  286:             }
  287:         }
  288:       else
  289:         {
  290:           /* We write the dataset before inserting it to the database
  291:              since while inserting this thread might block and so would
  292:              unnecessarily let the receiver wait.  */
  293:           assert (fd != -1);
  294: 
  295: #ifdef HAVE_SENDFILE
  296:           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
  297:             {
  298:               assert (db->wr_fd != -1);
  299:               assert ((char *) &dataset->resp > (char *) db->data);
  300:               assert ((char *) &dataset->resp - (char *) db->head
  301:                       + total
  302:                       <= (sizeof (struct database_pers_head)
  303:                           + db->head->module * sizeof (ref_t)
  304:                           + db->head->data_size));
  305:               written = sendfileall (fd, db->wr_fd,
  306:                                      (char *) &dataset->resp
  307:                                      - (char *) db->head, total);
  308: # ifndef __ASSUME_SENDFILE
  309:               if (written == -1 && errno == ENOSYS)
  310:                 goto use_write;
  311: # endif
  312:             }
  313:           else
  314: # ifndef __ASSUME_SENDFILE
  315:           use_write:
  316: # endif
  317: #endif
  318:             written = writeall (fd, &dataset->resp, total);
  319:         }
  320: 
  321: 
  322:       /* Add the record to the database.  But only if it has not been
  323:          stored on the stack.  */
  324:       if (! alloca_used)
  325:         {
  326:           /* If necessary, we also propagate the data to disk.  */
  327:           if (db->persistent)
  328:             {
  329:               // XXX async OK?
  330:               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
  331:               msync ((void *) pval,
  332:                      ((uintptr_t) dataset & pagesize_m1) + total + n,
  333:                      MS_ASYNC);
  334:             }
  335: 
  336:           /* Now get the lock to safely insert the records.  */
  337:           pthread_rwlock_rdlock (&db->lock);
  338: 
  339:           /* NB: in the following code we always must add the entry
  340:              marked with FIRST first.  Otherwise we end up with
  341:              dangling "pointers" in case a latter hash entry cannot be
  342:              added.  */
  343:           bool first = true;
  344: 
  345:           /* If the request was by UID, add that entry first.  */
  346:           if (req->type == GETPWBYUID)
  347:             {
  348:               if (cache_add (GETPWBYUID, cp, key_offset, &dataset->head, true,
  349:                              db, owner) < 0)
  350:                 {
  351:                   /* Could not allocate memory.  Make sure the data gets
  352:                      discarded.  */
  353:                   dataset->head.usable = false;
  354:                   goto out;
  355:                 }
  356: 
  357:               first = false;
  358:             }
  359:           /* If the key is different from the name add a separate entry.  */
  360:           else if (strcmp (key_copy, dataset->strdata) != 0)
  361:             {
  362:               if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
  363:                              &dataset->head, true, db, owner) < 0)
  364:                 {
  365:                   /* Could not allocate memory.  Make sure the data gets
  366:                      discarded.  */
  367:                   dataset->head.usable = false;
  368:                   goto out;
  369:                 }
  370: 
  371:               first = false;
  372:             }
  373: 
  374:           /* We have to add the value for both, byname and byuid.  */
  375:           if ((req->type == GETPWBYNAME || db->propagate)
  376:               && __builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
  377:                                               pw_name_len, &dataset->head,
  378:                                               first, db, owner) == 0, 1))
  379:             {
  380:               if (req->type == GETPWBYNAME && db->propagate)
  381:                 (void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
  382:                                   req->type != GETPWBYNAME, db, owner);
  383:             }
  384:           else if (first)
  385:             /* Could not allocate memory.  Make sure the data gets
  386:                discarded.  */
  387:             dataset->head.usable = false;
  388: 
  389:         out:
  390:           pthread_rwlock_unlock (&db->lock);
  391:         }
  392:     }
  393: 
  394:   if (__builtin_expect (written != total, 0) && debug_level > 0)
  395:     {
  396:       char buf[256];
  397:       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
  398:                strerror_r (errno, buf, sizeof (buf)));
  399:     }
  400: }
  401: 
  402: 
  403: union keytype
  404: {
  405:   void *v;
  406:   uid_t u;
  407: };
  408: 
  409: 
  410: static int
  411: lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer,
  412:         size_t buflen, struct passwd **pwd)
  413: {
  414:   if (type == GETPWBYNAME)
  415:     return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd);
  416:   else
  417:     return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd);
  418: }
  419: 
  420: 
  421: static void
  422: addpwbyX (struct database_dyn *db, int fd, request_header *req,
  423:           union keytype key, const char *keystr, uid_t c_uid,
  424:           struct hashentry *he, struct datahead *dh)
  425: {
  426:   /* Search for the entry matching the key.  Please note that we don't
  427:      look again in the table whether the dataset is now available.  We
  428:      simply insert it.  It does not matter if it is in there twice.  The
  429:      pruning function only will look at the timestamp.  */
  430:   size_t buflen = 1024;
  431:   char *buffer = (char *) alloca (buflen);
  432:   struct passwd resultbuf;
  433:   struct passwd *pwd;
  434:   bool use_malloc = false;
  435:   int errval = 0;
  436: 
  437:   if (__builtin_expect (debug_level > 0, 0))
  438:     {
  439:       if (he == NULL)
  440:         dbg_log (_("Haven't found \"%s\" in password cache!"), keystr);
  441:       else
  442:         dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
  443:     }
  444: 
  445:   while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
  446:          && (errval = errno) == ERANGE)
  447:     {
  448:       errno = 0;
  449: 
  450:       if (__builtin_expect (buflen > 32768, 0))
  451:         {
  452:           char *old_buffer = buffer;
  453:           buflen *= 2;
  454:           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
  455:           if (buffer == NULL)
  456:             {
  457:               /* We ran out of memory.  We cannot do anything but
  458:                  sending a negative response.  In reality this should
  459:                  never happen.  */
  460:               pwd = NULL;
  461:               buffer = old_buffer;
  462: 
  463:               /* We set the error to indicate this is (possibly) a
  464:                  temporary error and that it does not mean the entry
  465:                  is not available at all.  */
  466:               errval = EAGAIN;
  467:               break;
  468:             }
  469:           use_malloc = true;
  470:         }
  471:       else
  472:         /* Allocate a new buffer on the stack.  If possible combine it
  473:            with the previously allocated buffer.  */
  474:         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
  475:     }
  476: 
  477:   /* Add the entry to the cache.  */
  478:   cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh, errval);
  479: 
  480:   if (use_malloc)
  481:     free (buffer);
  482: }
  483: 
  484: 
  485: void
  486: addpwbyname (struct database_dyn *db, int fd, request_header *req,
  487:              void *key, uid_t c_uid)
  488: {
  489:   union keytype u = { .v = key };
  490: 
  491:   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
  492: }
  493: 
  494: 
  495: void
  496: readdpwbyname (struct database_dyn *db, struct hashentry *he,
  497:                struct datahead *dh)
  498: {
  499:   request_header req =
  500:     {
  501:       .type = GETPWBYNAME,
  502:       .key_len = he->len
  503:     };
  504:   union keytype u = { .v = db->data + he->key };
  505: 
  506:   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
  507: }
  508: 
  509: 
  510: void
  511: addpwbyuid (struct database_dyn *db, int fd, request_header *req,
  512:             void *key, uid_t c_uid)
  513: {
  514:   char *ep;
  515:   uid_t uid = strtoul ((char *) key, &ep, 10);
  516: 
  517:   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
  518:     {
  519:       if (debug_level > 0)
  520:         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
  521: 
  522:       errno = EINVAL;
  523:       return;
  524:     }
  525: 
  526:   union keytype u = { .u = uid };
  527: 
  528:   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
  529: }
  530: 
  531: 
  532: void
  533: readdpwbyuid (struct database_dyn *db, struct hashentry *he,
  534:               struct datahead *dh)
  535: {
  536:   char *ep;
  537:   uid_t uid = strtoul (db->data + he->key, &ep, 10);
  538: 
  539:   /* Since the key has been added before it must be OK.  */
  540:   assert (*(db->data + he->key) != '\0' && *ep == '\0');
  541: 
  542:   request_header req =
  543:     {
  544:       .type = GETPWBYUID,
  545:       .key_len = he->len
  546:     };
  547:   union keytype u = { .u = uid };
  548: 
  549:   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
  550: }
1
Syntax (Markdown)