Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

tc_path.c

Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 2004, 2005 by Kevin L. Mitchell <klmitch@mit.edu>
00003 **
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 **
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 **
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software
00016 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 **
00018 ** @(#)$Id: tc_path.c,v 1.7 2005/06/08 13:17:36 klmitch Exp $
00019 */
00027 #include <errno.h>
00028 #include <pwd.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <sys/types.h>
00032 #include <unistd.h>
00033 
00034 #include "treeconf_int.h"
00035 
00036 RCSTAG("@(#)$Id: tc_path.c,v 1.7 2005/06/08 13:17:36 klmitch Exp $");
00037 
00043 struct buf {
00044   char             *str;      
00045   int       len;  
00046   int       size; 
00047 };
00048 
00056 #define BUF_CHUNK       256
00057 
00073 static unsigned int
00074 _get_pwd(struct passwd **pwd_p, const char *uname, int len)
00075 {
00076   struct passwd *pwd = 0;
00077   char ubuf[512];
00078 
00079   if (uname) {
00080     strncpy(ubuf, uname, len); /* copy the name so we can nul-terminate... */
00081     ubuf[len] = '\0';
00082     pwd = getpwnam(ubuf); /* Get password entry by name */
00083   } else
00084     pwd = getpwuid(geteuid()); /* Get password entry by effective uid */
00085 
00086   if (!pwd) /* couldn't find the entry, it seems... */
00087     return !errno ? ENOENT : errno; /* errno can sometimes be 0 :/ */
00088 
00089   *pwd_p = pwd; /* return the entry... */
00090 
00091   return 0;
00092 }
00093 
00110 static unsigned int
00111 _add_str(struct buf *buf, const char *str, int len)
00112 {
00113   char *tmp = 0;
00114 
00115   if (!str) { /* empty str means insert user name */
00116     struct passwd *pwd = 0;
00117     unsigned int err;
00118 
00119     if ((err = _get_pwd(&pwd, 0, 0))) /* get password entry... */
00120       return err;
00121 
00122     str = pwd->pw_name; /* string is the password entry... */
00123     len = strlen(str);
00124   }
00125 
00126   if (len < 0) /* get length... */
00127     len = strlen(str);
00128 
00129   if (buf->size < buf->len + len + 1) {
00130     while (buf->size < buf->len + len) /* how much space do we need? */
00131       buf->size += BUF_CHUNK;
00132 
00133     /* Allocate the necessary memory... */
00134     if (!(tmp = (char *)realloc(buf->str, buf->size)))
00135       return ENOMEM;
00136     else
00137       buf->str = tmp;
00138   }
00139 
00140   strncpy(buf->str + buf->len, str, len); /* copy the string... */
00141   buf->len += len; /* update the buffer length... */
00142   buf->str[buf->len] = '\0'; /* and terminate the string */
00143 
00144   return 0;
00145 }
00146 
00162 static unsigned int
00163 _tilde(struct buf *buf, const char *uname, int len)
00164 {
00165   struct passwd *pwd = 0;
00166   unsigned int err;
00167 
00168   return (err = _get_pwd(&pwd, uname, len)) ? err :
00169     _add_str(buf, pwd->pw_dir, -1);
00170 }
00171 
00179 #define STOP                  0
00180 
00188 #define CONTINUE        1
00189 
00199 #define stop(e)               do { *err = (e); return STOP; } while (0)
00200 
00207 #define cont()                do { *err = 0; return CONTINUE; } while (0)
00208 
00216 #define _TC_PATH_DEFAULT      0x04
00217 
00225 #define _TC_PATH_PERCENT      0x08
00226 
00256 static int
00257 _subst(struct buf *buf, treeconf_str_t *element,
00258        treeconf_subst_t substs[], int s_cnt, unsigned int flags,
00259        treeconf_file_t call, void *call_data, unsigned int *err)
00260 {
00261   const char *str = 0, *tmp = 0;
00262   unsigned int _e = 0;
00263   int i, j, len = 0;
00264 
00265   if (!element->ts_length) /* skip empty components--shouldn't happen */
00266     cont();
00267 
00268   if (*element->ts_string == '~') /* Now, check for ~ expansion... */
00269     switch (element->ts_length > 1 ? element->ts_string[1] : '\0') {
00270     case '/': case '\0':
00271       if (flags & TC_PATH_SECURE)
00272       cont(); /* ~ is insecure in path string... */
00273       if ((_e = _tilde(buf, 0, 0))) /* tilde-expand for current uid */
00274       stop(_e);
00275       /* get the remainder of the string... */
00276       str = element->ts_length > 1 ? element->ts_string + 1 : 0;
00277       len = element->ts_length > 1 ? element->ts_length - 1 : 0;
00278       break;
00279     default:
00280       /* ~name is secure assuming that the path is trusted */
00281       for (i = 1; i < element->ts_length; i++) /* Find end of user name */
00282       if (element->ts_string[i] == '/')
00283         break;
00284       /* Now tilde-expand for that user */
00285       if ((_e = _tilde(buf, element->ts_string + 1, i - 2)))
00286       stop(_e);
00287       /* Get the remainder of the string... */
00288       str = i < element->ts_length ? element->ts_string + i : 0;
00289       len = i < element->ts_length ? element->ts_length - i : 0;
00290       break;
00291     }
00292   else {
00293     str = element->ts_string; /* Get the string */
00294     len = element->ts_length;
00295   }
00296 
00297   /* Now process the rest of the substitutions... */
00298   for (i = 0, tmp = str; i < len; i++)
00299     if (flags & _TC_PATH_PERCENT) {
00300       flags &= ~_TC_PATH_PERCENT; /* character following %... */
00301       if (str[i] == '%') /* it's an escaped % */
00302       tmp = str + i; /* Start constant string from here */
00303       else /* Search the substitutions... */
00304       for (j = 0; j < s_cnt; j++)
00305         if (str[i] == substs[j].tu_char) {
00306           if ((substs[j].tu_flags & TC_SUBST_IGNORE) ||
00307             (flags & (substs[j].tu_flags & TC_SUBST_INSECURE)))
00308             cont(); /* if it's insecure, skip it... */
00309           else if ((_e = _add_str(buf, substs[j].tu_value, -1)))
00310             stop(_e); /* Add the string... */
00311           break; /* get out of inner loop... */
00312         }
00313       /* If it's an undefined substitution, just skip it */
00314     } else if (str[i] == '%') {
00315       if ((_e = _add_str(buf, tmp, (str + i) - tmp))) /* Add constant string */
00316       stop(_e);
00317       flags |= _TC_PATH_PERCENT; /* Remember we found a % */
00318       tmp = 0;
00319     } else if (!tmp)
00320       tmp = str + i; /* Start constant string from here */
00321 
00322   if (flags & _TC_PATH_PERCENT) /* trailing %? */
00323     stop(EINVAL); /* error out... */
00324   else if (tmp && (_e = _add_str(buf, tmp, (str + i) - tmp)))
00325     stop(_e); /* Add trailing text... */
00326 
00327   /* Not safe (nor even intended to be safe!) against symlink attacks!  Do
00328    * not include files in /tmp or similar user-writable directories in the
00329    * path.
00330    */
00331   if (access(buf->str, R_OK)) { /* can we read file? */
00332     *err = 0; /* clear error... */
00333     buf->str[0] = '\0';
00334     buf->len = 0; /* and buffer... */
00335     cont();
00336   }
00337 
00338   if ((_e = (call)(buf->str, call_data))) /* call callback */
00339     stop(_e);
00340 
00341   *err = 0; /* clear error... */
00342   buf->str[0] = '\0';
00343   buf->len = 0; /* and buffer... */
00344 
00345   return flags & TC_PATH_ALL ? CONTINUE : STOP;
00346 }
00347 
00348 unsigned int
00349 tc_path(const char *path, const char *def,
00350       treeconf_subst_t substs[], int s_cnt, unsigned int flags,
00351       treeconf_file_t call, void *call_data)
00352 {
00353   treeconf_str_t *p_str = 0, *d_str = 0;
00354   struct buf buf = { 0, 0, 0 };
00355   int p_cnt, d_cnt, i, j;
00356   unsigned int err = 0;
00357 
00358   initialize_trcf_error_table();
00359 
00360   /* Sanity-check the arguments */
00361   if (!def || (s_cnt && !substs) || !call)
00362     return EINVAL;
00363 
00364   flags &= TC_PATH_SECURE | TC_PATH_ALL; /* set up flags... */
00365 
00366   /* If the path isn't specified, fall back to the default */
00367   if (!path) {
00368     path = def;
00369     flags |= _TC_PATH_DEFAULT;
00370   }
00371 
00372   /* Break up the path... */
00373   if ((err = tc_break(&p_str, &p_cnt, path, ":")))
00374     goto error;
00375 
00376   /* Search the path... */
00377   for (i = 0; i < p_cnt; i++)
00378     if (!p_str[i].ts_length) { /* Empty field...hmmm... */
00379       if (flags & _TC_PATH_DEFAULT)
00380       continue; /* skip empty fields after default searched */
00381       flags |= _TC_PATH_DEFAULT;
00382       if ((err = tc_break(&d_str, &d_cnt, def, ":"))) /* break up default... */
00383       goto error;
00384       /* Now search the default path... */
00385       for (j = 0; j < d_cnt; j++)
00386       if (!d_str[j].ts_length) /* skip empty fields in default */
00387         continue;
00388       else if (_subst(&buf, d_str + j, substs, s_cnt, flags, call,
00389                   call_data, &err) != CONTINUE)
00390         goto error; /* have to break out of nested for loop in this case */
00391     } else if (_subst(&buf, p_str + i, substs, s_cnt, flags, call,
00392                   call_data, &err) != CONTINUE)
00393       break; /* Only have to break out of enclosing for loop here... */
00394 
00395  error:
00396   if (p_str) /* Free allocated memory... */
00397     free(p_str);
00398   if (d_str)
00399     free(d_str);
00400   if (buf.str)
00401     free(buf.str);
00402 
00403   return err; /* And return! */
00404 }

Generated on Wed Jun 8 09:18:27 2005 for treeconf by  doxygen 1.3.9.1