1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 /*
  12  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  13  */
  14 #include <stdio.h>
  15 #include <sys/types.h>
  16 #include <sys/socket.h>
  17 #include <netinet/in.h>
  18 #include <arpa/inet.h>
  19 #include <netdb.h>
  20 #include <sys/types.h>
  21 #include <sys/stat.h>
  22 #include <unistd.h>
  23 #include <stdlib.h>
  24 #include <strings.h>
  25 #include <ctype.h>
  26 #include <thread.h>
  27 #include <synch.h>
  28 #include <time.h>
  29 #include <revnetgroup.h>
  30 
  31 #include "files_common.h"
  32 
  33 #define PATH_NETGROUP           "/etc/netgroup"
  34 
  35 static uint_t
  36 hash_netgr_host(nss_XbyY_args_t *argp, int keyhash, const char *line,
  37         int linelen)
  38 {
  39         const char *end;
  40         uint_t hash = 0;
  41         int power = 0;
  42 
  43         if (keyhash) {
  44                 struct nss_innetgr_args *ia;
  45 
  46                 ia = (struct nss_innetgr_args *)argp->key.name;
  47 
  48                 line = ia->arg[NSS_NETGR_MACHINE].argv[0];
  49                 end = line + strlen(line);
  50         } else {
  51                 const char *s;
  52 
  53                 end = strchr(line, '\t');
  54                 if (end == NULL)
  55                         end = line;
  56 
  57                 s = strstr(line, ".*");
  58                 if (s != NULL && s < end) {
  59                         end = s;
  60                 }
  61 
  62         }
  63 
  64         while (line < end) {
  65                 uint_t c = *line++;
  66 
  67                 c <<= power++;
  68                 hash += c;
  69         }
  70 
  71         return (hash);
  72 }
  73 
  74 static files_hash_func hash_netgr[] = { hash_netgr_host };
  75 
  76 static files_hash_t hashinfo = {
  77         DEFAULTMUTEX,
  78         0,
  79         NSS_BUFLEN_GROUP,
  80         1,
  81         hash_netgr
  82 };
  83 
  84 /* ARGSUSED */
  85 static int
  86 dummy_netgr(const char *in, int inlen,
  87     void *ent, char *buf, int buflen)
  88 {
  89         return (NSS_STR_PARSE_SUCCESS);
  90 }
  91 
  92 /*
  93  * return parsed or not
  94  */
  95 static boolean_t
  96 netgr_extract_hostname(char *str, char **hostname, char **next)
  97 {
  98         char *s;
  99 
 100         s = strchr(str, '\t');
 101         if (s == NULL || s == str)
 102                 return (B_FALSE);
 103 
 104         *s = '\0';
 105         *hostname = str;
 106         *next = s + 1;
 107 
 108         return (B_TRUE);
 109 }
 110 
 111 static boolean_t
 112 netgr_hosts_match(const char *hostname, const char *host, const char *domain)
 113 {
 114         char buf[NSS_BUFSIZ];
 115         uint_t n;
 116 
 117         /*
 118          * hostname can be "a.b.c.*"
 119          */
 120         n = strlen(hostname);
 121         if (n > 0 && hostname[n - 1] == '*')
 122                 domain = "*";
 123 
 124         snprintf(buf, sizeof (buf), "%s.%s", host, domain);
 125 
 126         if (strcmp(hostname, buf))
 127                 return (B_FALSE);
 128         else
 129                 return (B_TRUE);
 130 }
 131 
 132 static boolean_t
 133 netgr_netgroups_match(const char *netgroups, const char *group)
 134 {
 135         char *s;
 136         uint_t len;
 137 
 138         /* walk netgroups */
 139 
 140         s = strstr(netgroups, group);
 141         if (s == NULL)
 142                 return (B_FALSE);
 143 
 144         /* check beginning */
 145         if (s != netgroups && s[-1] != ',')
 146                 return (B_FALSE);
 147 
 148         /* check end */
 149         len = strlen(group);
 150         if (s[len] != ',' && s[len] != '\0')
 151                 return (B_FALSE);
 152 
 153         return (B_TRUE);
 154 }
 155 
 156 /*
 157  * Parse string: "{hostname} {netgroup1} ... {netgroup2}"
 158  */
 159 static int
 160 check_netgr(nss_XbyY_args_t *argp, const char *line, int linelen)
 161 {
 162         struct nss_innetgr_args *ia;
 163         char buf[NSS_BUFSIZ];
 164         const char *host;
 165         const char *domain;
 166         const char *group;
 167         const char *netgroup;
 168         char *hostname, *next;
 169 
 170         if (linelen > NSS_BUFSIZ || linelen < 0)
 171                 return (0);
 172 
 173         /* use only one passed argument */
 174         ia = (struct nss_innetgr_args *)argp->key.name;
 175         host = ia->arg[NSS_NETGR_MACHINE].argv[0];
 176         domain = ia->arg[NSS_NETGR_DOMAIN].argv[0];
 177         netgroup = ia->groups.argv[0];
 178 
 179         strlcpy(buf, line, NSS_BUFSIZ);
 180 
 181         if (!netgr_extract_hostname(buf, &hostname, &next))
 182                 return (0);
 183 
 184         if (!netgr_hosts_match(hostname, host, domain))
 185                 return (0);
 186 
 187         if (!netgr_netgroups_match(next, netgroup))
 188                 return (0);
 189 
 190         return (1);
 191 }
 192 
 193 static nss_status_t
 194 netgr_in(files_backend_ptr_t be, void *a)
 195 {
 196         struct nss_innetgr_args *ia = (struct nss_innetgr_args *)a;
 197         nss_status_t    rc = (nss_status_t)NSS_NOTFOUND;
 198         nss_XbyY_args_t xa = {0};
 199         struct stat64 st;
 200         int retries;
 201 
 202         retries = 5;
 203         while (stat64(PATH_NETGROUP, &st) < 0) {
 204                 struct timespec delay;
 205 
 206                 /* 50 ms */
 207                 delay.tv_sec = 0;
 208                 delay.tv_nsec = 50000000;
 209 
 210                 nanosleep(&delay, NULL);
 211                 if (--retries == 0)
 212                         goto out;
 213         }
 214 
 215         mutex_lock(&hashinfo.fh_lock);
 216 
 217         if ((uint_t)st.st_mtim.tv_sec > (uint_t)hashinfo.fh_mtime.tv_sec) {
 218                 FILE *fin, *fout;
 219 
 220                 /*
 221                  * netgroup file is more recent than parsed copy
 222                  */
 223 
 224                 fin = fopen(PATH_NETGROUP, "r");
 225                 if (fin == NULL)
 226                         goto out_unlock;
 227 
 228                 fout = fopen(be->filename, "w");
 229                 if (fout == NULL) {
 230                         fclose(fin);
 231                         goto out_unlock;
 232                 }
 233 
 234                 revnetgroup_handle(fin, fout, B_FALSE);
 235 
 236                 fclose(fout);
 237                 fclose(fin);
 238         }
 239 
 240 out_unlock:
 241         mutex_unlock(&hashinfo.fh_lock);
 242 
 243         /* some hack to pass arguments to check_netgr */
 244         xa.key.name = (char *)ia;
 245         xa.str2ent = dummy_netgr;
 246         rc = _nss_files_XY_hash(be, &xa, 1, &hashinfo, 0, check_netgr);
 247 
 248 out:
 249         ia->status = (rc == NSS_SUCCESS) ? NSS_NETGR_FOUND : NSS_NETGR_NO;
 250         return (rc);
 251 }
 252 
 253 /*ARGSUSED*/
 254 nss_status_t
 255 netgr_files_destr(files_backend_ptr_t be, void *dummy)
 256 {
 257         nss_status_t status;
 258 
 259         unlink(be->filename);
 260         free((void *)be->filename);
 261         status = _nss_files_destr(be, dummy);
 262         return (status);
 263 }
 264 
 265 static char *
 266 create_uniq_filename(void)
 267 {
 268         char *filename;
 269         int dummy_fd;
 270 
 271         filename = malloc(PATH_MAX);
 272         if (filename == NULL)
 273                 return (NULL);
 274 
 275         strlcpy(filename, "/tmp/revnetgroup-XXXXXX", PATH_MAX);
 276 
 277         dummy_fd = mkstemp(filename);
 278         close(dummy_fd);
 279         if (dummy_fd < 0) {
 280                 free(filename);
 281                 return (NULL);
 282         }
 283         return (filename);
 284 }
 285 
 286 static files_backend_op_t netgr_ops[] = {
 287         netgr_files_destr,
 288         NULL,                   /* No endent, because no setent */
 289         NULL,                   /* No setent */
 290         NULL,                   /* No getent_netdb */
 291         netgr_in,               /* innetgr */
 292         NULL,                   /* No setnetgrent */
 293 };
 294 
 295 /*ARGSUSED*/
 296 nss_backend_t *
 297 _nss_files_netgroup_constr(const char *dummy1,
 298     const char *dummy2, const char *dummy3)
 299 {
 300         char *filename;
 301 
 302         filename = create_uniq_filename();
 303         if (filename == NULL)
 304                 return (NULL);
 305 
 306         return (_nss_files_constr(netgr_ops,
 307             sizeof (netgr_ops) / sizeof (netgr_ops[0]),
 308             filename,
 309             NSS_LINELEN_NETWORKS,
 310             NULL));
 311 }