1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2015 Gary Mills
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
  28 /*        All Rights Reserved   */
  29 
  30 /*
  31  * Portions of this source code were derived from Berkeley 4.3 BSD
  32  * under license from the Regents of the University of California.
  33  */
  34 
  35 #include <stdlib.h>
  36 #include <dirent.h>
  37 #include <strings.h>
  38 #include "ypsym.h"
  39 #include "ypdefs.h"
  40 USE_YPDBPATH
  41 USE_DBM
  42 #include "shim.h"
  43 #include "../ldap_util.h"
  44 
  45 /*
  46  * This constructs a file name from a passed domain name, a passed map name,
  47  * and a globally known YP data base path prefix.
  48  *
  49  * Has to be in shim because it needs the N2L prefix
  50  *
  51  * RETURNS :    TRUE = A name was successfully created
  52  *              FALSE = A name could not be created
  53  */
  54 
  55 bool_t
  56 ypmkfilename(domain, map, path)
  57         char *domain;
  58         char *map;
  59         char *path;
  60 {
  61         int length;
  62 
  63         /* Do not allow any path as a domain name. */
  64         if (strchr(domain, '/') != NULL)
  65                 return (FALSE);
  66 
  67         length = strlen(domain) + strlen(map) + ypdbpath_sz + 3;
  68         if (yptol_mode)
  69                 length += strlen(NTOL_PREFIX) + 1;
  70 
  71         if ((MAXNAMLEN + 1) < length) {
  72                 (void) fprintf(stderr, "ypserv:  Map name string too long.\n");
  73                 return (FALSE);
  74         }
  75 
  76         strcpy(path, ypdbpath);
  77         strcat(path, "/");
  78         strcat(path, domain);
  79         strcat(path, "/");
  80 
  81         /* If in N2L mode add N2L prefix */
  82         if (yptol_mode)
  83                 strcat(path, NTOL_PREFIX);
  84         strcat(path, map);
  85 
  86         return (TRUE);
  87 }
  88 
  89 /*
  90  * check whether a map is already in an array/list
  91  *
  92  * RETURNS: TRUE if yes
  93  *          FALSE if not
  94  */
  95 bool_t
  96 on_maplist(char *mapname, char **list) {
  97         int i = 0;
  98 
  99         if (list == NULL) {
 100                 return (FALSE);
 101         }
 102 
 103         while (list[i] != NULL) {
 104                 if (strcmp(mapname, list[i++]) == 0) {
 105                         return (TRUE);
 106                 }
 107         }
 108 
 109         return (FALSE);
 110 }
 111 
 112 /*
 113  * add a map at the end of an array/list
 114  *
 115  * list_len: if -1, we do not know list length
 116  *
 117  * RETURNS: TRUE if map was added
 118  *          FALSE if not
 119  */
 120 bool_t
 121 add_in_maplist(char *mapname, char ***list, int *list_len) {
 122         int i = 0;
 123         char **list_tmp;
 124 
 125         if (list == NULL) {
 126                 return (FALSE);
 127         }
 128 
 129         list_tmp = *list;
 130 
 131         if (list_tmp == NULL) {
 132                 *list_len = 0;
 133         } else {
 134                 /* find 1st free element */
 135                 while (list_tmp[i] != NULL) {
 136                         /*
 137                          * increment in loop so that
 138                          * list_tmp[i] == NULL
 139                          * when exiting
 140                          */
 141                         i++;
 142                 }
 143         }
 144 
 145         /* if we don't know list length, assume we reach its end */
 146         if (*list_len == -1) {
 147                 *list_len = i;
 148         }
 149 
 150         /* do we need to reallocate ? */
 151         if (i+1 >= *list_len) {
 152                 list_tmp = (char **)realloc(list_tmp,
 153                                     (*list_len + ARRAY_CHUNK) *
 154                                         sizeof (char *));
 155                 if (list_tmp == NULL) {
 156                         return (FALSE);
 157                 }
 158                 *list = list_tmp;
 159                 *list_len += ARRAY_CHUNK;
 160         }
 161 
 162         /* add in list */
 163         (*list)[i] = strdup(mapname);
 164         if ((*list)[i] == NULL) {
 165                 /* strdup() failed */
 166                 return (FALSE);
 167         }
 168         (*list)[++i] = NULL;
 169 
 170         return (TRUE);
 171 }
 172 
 173 /*
 174  * This checks to see whether a domain name is present at the local node as a
 175  * subdirectory of ypdbpath
 176  *
 177  * Was originally in cmd/ypcmd/shared/ancil.c as ypcheck_domain(domain).
 178  * Now ypcheck_domain(domain) calls this function.
 179  */
 180 bool
 181 ypcheck_domain_yptol(char *domain)
 182 {
 183         char path[MAXNAMLEN + 1];
 184         struct stat filestat;
 185         bool present = FALSE;
 186 
 187         strcpy(path, ypdbpath);
 188         strcat(path, "/");
 189         if (strlcat(path, domain, MAXNAMLEN + 1) >=  MAXNAMLEN + 1)
 190                 return (present);
 191 
 192         if (stat(path, &filestat) != -1) {
 193                 if (S_ISDIR(filestat.st_mode))
 194                         present = TRUE;
 195         }
 196         return (present);
 197 }
 198 
 199 /*
 200  * This performs an existence check on the dbm data base files <name>.pag and
 201  * <name>.dir.  pname is a ptr to the filename.  This should be an absolute
 202  * path.
 203  * Returns TRUE if the map exists and is accessible; else FALSE.
 204  *
 205  * Note:  The file name should be a "base" form, without a file "extension" of
 206  * .dir or .pag appended.  See ypmkfilename for a function which will generate
 207  * the name correctly.  Errors in the stat call will be reported at this level,
 208  * however, the non-existence of a file is not considered an error, and so will
 209  * not be reported.
 210  *
 211  * Was originally in cmd/ypcmd/shared/utils.c as ypcheck_map_existence().
 212  * Now ypcheck_map_existence() calls this function.
 213  */
 214 bool
 215 ypcheck_map_existence_yptol(char *pname)
 216 {
 217         char dbfile[MAXNAMLEN + sizeof (TTL_POSTFIX) + 1];
 218         struct stat64 filestat;
 219         int len;
 220 
 221         if (!pname || ((len = (int)strlen(pname)) == 0) ||
 222             (len + sizeof (dbm_pag) + sizeof (TTL_POSTFIX)) >
 223             sizeof (dbfile)) {
 224                 return (FALSE);
 225         }
 226 
 227         errno = 0;
 228 
 229         /* Check for existance of .dir file */
 230         (void) strcpy(dbfile, pname);
 231         (void) strcat(dbfile, dbm_dir);
 232 
 233         if (stat64(dbfile, &filestat) == -1) {
 234                 if (errno != ENOENT) {
 235                         (void) fprintf(stderr,
 236                             "ypserv:  Stat error on map file %s.\n",
 237                             dbfile);
 238                 }
 239                 return (FALSE);
 240         }
 241 
 242         /* Check for existance of .pag file */
 243         (void) strcpy(dbfile, pname);
 244         (void) strcat(dbfile, dbm_pag);
 245 
 246         if (stat64(dbfile, &filestat) == -1) {
 247                 if (errno != ENOENT) {
 248                         (void) fprintf(stderr,
 249                             "ypserv:  Stat error on map file %s.\n",
 250                             dbfile);
 251                 }
 252                 return (FALSE);
 253         }
 254 
 255         if (yptol_mode) {
 256                 /* Check for existance of TTL .dir file */
 257                 (void) strcpy(dbfile, pname);
 258                 (void) strcat(dbfile, TTL_POSTFIX);
 259                 (void) strcat(dbfile, dbm_dir);
 260 
 261                 if (stat64(dbfile, &filestat) == -1) {
 262                         if (errno != ENOENT) {
 263                                 (void) fprintf(stderr,
 264                                     "ypserv:  Stat error on map file %s.\n",
 265                                     dbfile);
 266                         }
 267                         return (FALSE);
 268                 }
 269 
 270                 /* Check for existance of TTL .pag file */
 271                 (void) strcpy(dbfile, pname);
 272                 (void) strcat(dbfile, TTL_POSTFIX);
 273                 (void) strcat(dbfile, dbm_pag);
 274 
 275                 if (stat64(dbfile, &filestat) == -1) {
 276                         if (errno != ENOENT) {
 277                                 (void) fprintf(stderr,
 278                                     "ypserv:  Stat error on map file %s.\n",
 279                                     dbfile);
 280                         }
 281                         return (FALSE);
 282                 }
 283         }
 284 
 285         return (TRUE);
 286 }
 287 
 288 /*
 289  * This adds maps in a domain to a given list,
 290  * from maps in /var/yp/<domain>
 291  * Inspired from yplist_maps() in cmd/ypcmd/ypserv_ancil.c
 292  *
 293  * domain is the relevant domain name
 294  * map_list is the list of maps in an array of map names,
 295  *    which may or may not be empty
 296  *
 297  * RETURNS :    TRUE = everything went fine
 298  *              FALSE = an error occured
 299  */
 300 bool_t
 301 add_map_domain_to_list(char *domain, char ***map_list)
 302 {
 303         char domdir[MAXNAMLEN + 1];
 304         char path[MAXNAMLEN + 1];
 305         int domdir_len = sizeof (domdir);
 306         DIR *dirp;
 307         struct dirent *dp;
 308         int name_len;
 309         int dbm_pag_len = sizeof (dbm_pag);
 310         char *ext;
 311         char *mapname;
 312         int map_list_len = -1;
 313 
 314         if (map_list == NULL) {
 315                 return (FALSE);
 316         }
 317 
 318         /* no domain, not a problem */
 319         if (domain == NULL) {
 320                 return (TRUE);
 321         }
 322 
 323         /* not a valid domain, not a problem */
 324         if (!ypcheck_domain_yptol(domain)) {
 325                 return (TRUE);
 326         }
 327 
 328         if (snprintf(domdir, domdir_len, "%s/%s", ypdbpath, domain)
 329             > domdir_len) {
 330                 return (FALSE);
 331         }
 332 
 333         if ((dirp = opendir(domdir)) == NULL) {
 334                 return (FALSE);
 335         }
 336 
 337         for (dp = readdir(dirp); dp != NULL;
 338             dp = readdir(dirp)) {
 339                 /*
 340                  * If it's possible that the file name is one of the two files
 341                  * implementing a map, remove the extension (dbm_pag or dbm_dir)
 342                  */
 343                 name_len = (int)strlen(dp->d_name);
 344 
 345                 if (name_len < dbm_pag_len - 1) {
 346                         continue;               /* Too Short */
 347                 }
 348 
 349                 ext = &(dp->d_name[name_len - (dbm_pag_len - 1)]);
 350 
 351                 if (strcmp(ext, dbm_pag) != 0) {
 352                         continue;               /* No dbm file extension */
 353                 }
 354 
 355                 *ext = '\0';
 356 
 357                 /*
 358                  * In yptol mode look at LDAP_ prefixed maps. In non yptol mode
 359                  * ignore them.
 360                  */
 361                 if (yptol_mode) {
 362                         if (0 != strncmp(dp->d_name, NTOL_PREFIX,
 363                             strlen(NTOL_PREFIX))) {
 364                                 continue;
 365                         }
 366 
 367                         /*
 368                          * Already have an LDAP_ prefix. Don't want to add it
 369                          * twice.
 370                          */
 371                         mapname = dp->d_name + strlen(NTOL_PREFIX);
 372                 } else {
 373                         if (0 == strncmp(dp->d_name, NTOL_PREFIX,
 374                             strlen(NTOL_PREFIX))) {
 375                                 continue;
 376                         }
 377                         mapname = dp->d_name;
 378                 }
 379 
 380                 if (ypmkfilename(domain, mapname, path) == FALSE) {
 381                         (void) closedir(dirp);
 382                         return (FALSE);
 383                 }
 384 
 385                 /*
 386                  * At this point, path holds the map file base name (no dbm
 387                  * file extension), and mapname holds the map name.
 388                  */
 389                 if (ypcheck_map_existence_yptol(path) &&
 390                     !on_maplist(mapname, *map_list)) {
 391                         if (add_in_maplist(mapname, map_list, &map_list_len) ==
 392                             FALSE) {
 393                                 (void) closedir(dirp);
 394                                 return (FALSE);
 395                         }
 396                 }
 397         }
 398 
 399         (void) closedir(dirp);
 400         return (TRUE);
 401 }