LCOV - code coverage report
Current view: top level - src/plugins/passwd - passwd.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 129 158 81.6 %
Date: 2019-09-12 12:28:41 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for passwd plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "passwd.h"
      11             : 
      12             : #include <kdberrors.h>
      13             : #include <kdbhelper.h>
      14             : 
      15             : #include <pwd.h>
      16             : #include <stdio.h>
      17             : #include <stdlib.h>
      18             : #include <string.h>
      19             : #include <sys/stat.h>
      20             : #include <sys/types.h>
      21             : 
      22             : #define ID_MAX_CHARACTERS 11
      23             : 
      24             : typedef enum
      25             : {
      26             :         NAME,
      27             :         UID,
      28             : } SortBy;
      29             : 
      30             : // simple validation of passwd entries
      31          12 : static int validatepwent (struct passwd * pwd)
      32             : {
      33          12 :         if (!pwd->pw_name) return -1;
      34          12 :         if (strlen (pwd->pw_name) == 0) return -1;
      35          12 :         if (pwd->pw_name[0] == '-') // POSIX.1-2008 3.431 User Name
      36             :                 return -1;
      37             :         const char * invalidCharacters =
      38             :                 "/:;<=>?@[\\]^`"; // POSIX.1-2008 3.278 Portable File Character Set - invalid characters > 45 && <= 122
      39          48 :         for (char * ptr = pwd->pw_name; *ptr != '\0'; ++ptr)
      40             :         {
      41          48 :                 if ((*ptr < '-') || (*ptr > 'z') || (strchr (invalidCharacters, *ptr) != NULL)) return -1;
      42             :         }
      43          12 :         if (!pwd->pw_passwd) return -1;
      44          12 :         if (pwd->pw_uid == (uid_t) -1) return -1;
      45          12 :         if (pwd->pw_gid == (gid_t) -1) return -1;
      46          12 :         if (!pwd->pw_gecos) return -1;
      47          12 :         if (!pwd->pw_dir) return -1;
      48          12 :         if (!pwd->pw_shell) return -1;
      49          12 :         return 0;
      50             : }
      51             : 
      52             : #if defined(USE_FGETPWENT_LOCAL)
      53             : // clang-format off
      54             : /* Taken from musl libc
      55             :  *
      56             :  * https://github.com/ifduyue/musl/blob/b4b1e10364c8737a632be61582e05a8d3acf5690/src/passwd/getpwent_a.c
      57             :  * https://github.com/ifduyue/musl/blob/b4b1e10364c8737a632be61582e05a8d3acf5690/src/passwd/fgetpwent.c
      58             :  */
      59             : 
      60             : static unsigned atou(char **s)
      61             : {
      62             :         unsigned x;
      63             :         for (x=0; (unsigned char)(**s-'0')<10U; ++*s) x=10*x+((unsigned char)**s-'0');
      64             :         return x;
      65             : }
      66             : 
      67             : int __getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size, struct passwd **res)
      68             : {
      69             :         ssize_t l;
      70             :         char *s;
      71             :         int rv = 0;
      72             :         for (;;) {
      73             :                 if ((l=getline(line, size, f)) < 0) {
      74             :                         rv = ferror(f) ? errno : 0;
      75             :                         free(*line);
      76             :                         *line = 0;
      77             :                         pw = 0;
      78             :                         break;
      79             :                 }
      80             :                 line[0][l-1] = 0;
      81             : 
      82             :                 s = line[0];
      83             :                 pw->pw_name = s++;
      84             :                 if (!(s = strchr(s, ':'))) continue;
      85             : 
      86             :                 *s++ = 0; pw->pw_passwd = s;
      87             :                 if (!(s = strchr(s, ':'))) continue;
      88             : 
      89             :                 *s++ = 0; pw->pw_uid = atou(&s);
      90             :                 if (*s != ':') continue;
      91             : 
      92             :                 *s++ = 0; pw->pw_gid = atou(&s);
      93             :                 if (*s != ':') continue;
      94             : 
      95             :                 *s++ = 0; pw->pw_gecos = s;
      96             :                 if (!(s = strchr(s, ':'))) continue;
      97             : 
      98             :                 *s++ = 0; pw->pw_dir = s;
      99             :                 if (!(s = strchr(s, ':'))) continue;
     100             : 
     101             :                 *s++ = 0; pw->pw_shell = s;
     102             :                 break;
     103             :         }
     104             :         *res = pw;
     105             :         if (rv) errno = rv;
     106             :         return rv;
     107             : }
     108             : 
     109             : struct passwd *fgetpwent_l(FILE *f)
     110             : {
     111             :         static char *line;
     112             :         static struct passwd pw;
     113             :         size_t size=0;
     114             :         struct passwd *res;
     115             :         __getpwent_a(f, &pw, &line, &size, &res);
     116             :         return res;
     117             : }
     118             : // clang-format on
     119             : #endif
     120             : 
     121          20 : static KeySet * pwentToKS (struct passwd * pwd, Key * parentKey, SortBy index)
     122             : {
     123          20 :         KeySet * ks = ksNew (0, KS_END);
     124          20 :         Key * append = keyNew (keyName (parentKey), KEY_END);
     125             :         char id[ID_MAX_CHARACTERS];
     126          20 :         if (index == UID)
     127             :         {
     128           0 :                 snprintf (id, sizeof (id), "%u", pwd->pw_uid);
     129           0 :                 keyAddBaseName (append, id);
     130           0 :                 keySetBinary (append, 0, 0);
     131           0 :                 ksAppendKey (ks, keyDup (append));
     132           0 :                 keyAddBaseName (append, "name");
     133           0 :                 keySetString (append, pwd->pw_name);
     134             :         }
     135             :         else
     136             :         {
     137          20 :                 keyAddBaseName (append, pwd->pw_name);
     138          20 :                 keySetBinary (append, 0, 0);
     139          20 :                 ksAppendKey (ks, keyDup (append));
     140          20 :                 snprintf (id, sizeof (id), "%u", pwd->pw_uid);
     141          20 :                 keyAddBaseName (append, "uid");
     142          20 :                 keySetString (append, id);
     143             :         }
     144          20 :         ksAppendKey (ks, keyDup (append));
     145          20 :         keySetString (append, 0);
     146          20 :         keySetBaseName (append, "shell");
     147          20 :         keySetString (append, pwd->pw_shell);
     148          20 :         ksAppendKey (ks, keyDup (append));
     149          20 :         keySetString (append, 0);
     150          20 :         keySetBaseName (append, "home");
     151          20 :         keySetString (append, pwd->pw_dir);
     152          20 :         ksAppendKey (ks, keyDup (append));
     153          20 :         keySetString (append, 0);
     154          20 :         keySetBaseName (append, "gid");
     155          20 :         snprintf (id, sizeof (id), "%u", pwd->pw_gid);
     156          20 :         keySetString (append, id);
     157          20 :         ksAppendKey (ks, keyDup (append));
     158          20 :         keySetString (append, 0);
     159          20 :         keySetBaseName (append, "passwd");
     160          20 :         keySetString (append, pwd->pw_passwd);
     161          20 :         ksAppendKey (ks, keyDup (append));
     162          20 :         keySetString (append, 0);
     163          20 :         keySetBaseName (append, "gecos");
     164          20 :         keySetString (append, pwd->pw_gecos);
     165          20 :         ksAppendKey (ks, keyDup (append));
     166          20 :         keyDel (append);
     167          20 :         return ks;
     168             : }
     169             : 
     170          24 : int elektraPasswdGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     171             : {
     172          24 :         if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/passwd"))
     173             :         {
     174          20 :                 KeySet * contract =
     175          20 :                         ksNew (30, keyNew ("system/elektra/modules/passwd", KEY_VALUE, "passwd plugin waits for your orders", KEY_END),
     176             :                                keyNew ("system/elektra/modules/passwd/exports", KEY_END),
     177             :                                keyNew ("system/elektra/modules/passwd/exports/get", KEY_FUNC, elektraPasswdGet, KEY_END),
     178             :                                keyNew ("system/elektra/modules/passwd/exports/set", KEY_FUNC, elektraPasswdSet, KEY_END),
     179             : #include ELEKTRA_README
     180             :                                keyNew ("system/elektra/modules/passwd/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     181          20 :                 ksAppend (returned, contract);
     182          20 :                 ksDel (contract);
     183             : 
     184          20 :                 return 1; // success
     185             :         }
     186             :         // get all keys
     187             :         SortBy index;
     188           4 :         KeySet * config = elektraPluginGetConfig (handle);
     189           4 :         Key * sortByKey = ksLookupByName (config, "/index", 0);
     190           4 :         if (sortByKey)
     191             :         {
     192           4 :                 if (!strcmp (keyString (sortByKey), "uid"))
     193             :                         index = UID;
     194           4 :                 else if (!strcmp (keyString (sortByKey), "name"))
     195             :                         index = NAME;
     196             :                 else
     197           0 :                         index = UID;
     198             :         }
     199             :         else
     200             :                 index = UID;
     201             :         struct passwd * pwd;
     202           4 :         FILE * pwfile = fopen (keyString (parentKey), "r");
     203           4 :         if (!pwfile)
     204             :         {
     205           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open configuration file %s. Reason: %s\n", keyString (parentKey),
     206             :                                              strerror (errno));
     207           0 :                 return -1;
     208             :         }
     209             : #if defined(USE_FGETPWENT)
     210          24 :         while ((pwd = fgetpwent (pwfile)) != NULL)
     211             : #elif defined(USE_FGETPWENT_LOCAL)
     212             :         while ((pwd = fgetpwent_l (pwfile)) != NULL)
     213             : #else
     214             : #error Configuration error in CMakeLists.txt. Neither fgetpwent nor getline were provided. Please open an issue at https://issue.libelektra.org.
     215             : #endif
     216             :         {
     217          20 :                 KeySet * ks = pwentToKS (pwd, parentKey, index);
     218          20 :                 ksAppend (returned, ks);
     219          20 :                 ksDel (ks);
     220             :         }
     221           4 :         fclose (pwfile);
     222           4 :         return 1; // success
     223             : }
     224             : 
     225          12 : static struct passwd * KStoPasswd (KeySet * ks, SortBy index)
     226             : {
     227          12 :         struct passwd * pwd = elektraMalloc (sizeof (struct passwd));
     228          12 :         ksRewind (ks);
     229          12 :         Key * parent = ksNext (ks);
     230          12 :         Key * lookup = keyDup (parent);
     231          12 :         Key * found = NULL;
     232          12 :         if (index == UID)
     233             :         {
     234           0 :                 found = ksLookup (ks, parent, 0);
     235           0 :                 if (!found)
     236           0 :                         pwd->pw_uid = (uid_t) -1;
     237             :                 else
     238           0 :                         pwd->pw_uid = atoi (keyBaseName (found));
     239           0 :                 keyAddBaseName (lookup, "name");
     240           0 :                 found = ksLookup (ks, lookup, 0);
     241           0 :                 if (!found)
     242           0 :                         pwd->pw_name = NULL;
     243             :                 else
     244           0 :                         pwd->pw_name = (char *) keyString (found);
     245             :         }
     246             :         else
     247             :         {
     248          12 :                 found = ksLookup (ks, parent, 0);
     249          12 :                 if (!found)
     250           0 :                         pwd->pw_name = NULL;
     251             :                 else
     252          12 :                         pwd->pw_name = (char *) keyBaseName (found);
     253          12 :                 keyAddBaseName (lookup, "uid");
     254          12 :                 found = ksLookup (ks, lookup, 0);
     255          12 :                 if (!found)
     256           0 :                         pwd->pw_uid = (uid_t) -1;
     257             :                 else
     258          24 :                         pwd->pw_uid = atoi (keyString (found));
     259             :         }
     260          12 :         keySetBaseName (lookup, "shell");
     261          12 :         found = ksLookup (ks, lookup, 0);
     262          12 :         if (!found)
     263           0 :                 pwd->pw_shell = NULL;
     264             :         else
     265          12 :                 pwd->pw_shell = (char *) keyString (found);
     266          12 :         keySetBaseName (lookup, "gid");
     267          12 :         found = ksLookup (ks, lookup, 0);
     268          12 :         if (!found)
     269           0 :                 pwd->pw_gid = (gid_t) -1;
     270             :         else
     271          24 :                 pwd->pw_gid = atoi (keyString (found));
     272          12 :         keySetBaseName (lookup, "home");
     273          12 :         found = ksLookup (ks, lookup, 0);
     274          12 :         if (!found)
     275           0 :                 pwd->pw_dir = NULL;
     276             :         else
     277          12 :                 pwd->pw_dir = (char *) keyString (found);
     278          12 :         keySetBaseName (lookup, "gecos");
     279          12 :         found = ksLookup (ks, lookup, 0);
     280          12 :         if (!found)
     281           0 :                 pwd->pw_gecos = "";
     282             :         else
     283          12 :                 pwd->pw_gecos = (char *) keyString (found);
     284          12 :         keySetBaseName (lookup, "passwd");
     285          12 :         found = ksLookup (ks, lookup, 0);
     286          12 :         if (!found)
     287           0 :                 pwd->pw_passwd = "";
     288             :         else
     289          12 :                 pwd->pw_passwd = (char *) keyString (found);
     290          12 :         keyDel (parent);
     291          12 :         keyDel (lookup);
     292          12 :         return pwd;
     293             : }
     294             : 
     295           4 : static int writeKS (KeySet * returned, Key * parentKey, SortBy index)
     296             : {
     297           4 :         FILE * pwfile = fopen (keyString (parentKey), "w");
     298           4 :         if (!pwfile)
     299             :         {
     300           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open %s for writing\n. Reason: %s", keyString (parentKey),
     301             :                                              strerror (errno));
     302           0 :                 return -1;
     303             :         }
     304             :         Key * cur;
     305           4 :         ksRewind (returned);
     306          20 :         while ((cur = ksNext (returned)) != NULL)
     307             :         {
     308          12 :                 if (!keyIsDirectBelow (parentKey, cur)) continue;
     309          12 :                 KeySet * cutKS = ksCut (returned, cur);
     310          12 :                 struct passwd * pwd = KStoPasswd (cutKS, index);
     311          12 :                 if (validatepwent (pwd) == -1)
     312             :                 {
     313           0 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Invalid passwd entry %s:%s:%u:%u:%s:%s:%s\n", pwd->pw_name,
     314             :                                                                  pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir,
     315             :                                                                  pwd->pw_shell);
     316             :                 }
     317             :                 else
     318             :                 {
     319             : #if defined(USE_PUTPWENT)
     320          12 :                         putpwent (pwd, pwfile);
     321             : #else
     322             :                         fprintf (pwfile, "%s:%s:%u:%u:%s:%s:%s\n", pwd->pw_name, pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos,
     323             :                                  pwd->pw_dir, pwd->pw_shell);
     324             : #endif
     325             :                 }
     326          12 :                 elektraFree (pwd);
     327          12 :                 ksAppend (returned, cutKS);
     328          12 :                 ksDel (cutKS);
     329             :         }
     330           4 :         fclose (pwfile);
     331           4 :         return 1;
     332             : }
     333             : 
     334           4 : int elektraPasswdSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     335             : {
     336             :         // get all keys
     337             :         // this function is optional
     338             :         SortBy index;
     339           4 :         KeySet * config = elektraPluginGetConfig (handle);
     340           4 :         Key * sortByKey = ksLookupByName (config, "/index", 0);
     341           4 :         if (sortByKey)
     342             :         {
     343           4 :                 if (!strcmp (keyString (sortByKey), "uid"))
     344             :                         index = UID;
     345           4 :                 else if (!strcmp (keyString (sortByKey), "name"))
     346             :                         index = NAME;
     347             :                 else
     348           0 :                         index = UID;
     349             :         }
     350             :         else
     351             :                 index = UID;
     352             : 
     353           4 :         int rc = writeKS (returned, parentKey, index);
     354             : 
     355           4 :         return rc; // success
     356             : }
     357             : 
     358          26 : Plugin * ELEKTRA_PLUGIN_EXPORT
     359             : {
     360             :         // clang-format off
     361          26 :         return elektraPluginExport ("passwd",
     362             :             ELEKTRA_PLUGIN_GET, &elektraPasswdGet,
     363             :             ELEKTRA_PLUGIN_SET, &elektraPasswdSet,
     364             :             ELEKTRA_PLUGIN_END);
     365             : }
     366             : 

Generated by: LCOV version 1.13