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