LCOV - code coverage report
Current view: top level - source4/heimdal/lib/krb5 - expand_path.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 39 94 41.5 %
Date: 2021-09-23 10:06:22 Functions: 3 6 50.0 %

          Line data    Source code
       1             : 
       2             : /***********************************************************************
       3             :  * Copyright (c) 2009, Secure Endpoints Inc.
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions
       8             :  * are met:
       9             :  *
      10             :  * - Redistributions of source code must retain the above copyright
      11             :  *   notice, this list of conditions and the following disclaimer.
      12             :  *
      13             :  * - Redistributions in binary form must reproduce the above copyright
      14             :  *   notice, this list of conditions and the following disclaimer in
      15             :  *   the documentation and/or other materials provided with the
      16             :  *   distribution.
      17             :  *
      18             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      21             :  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      22             :  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
      23             :  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      24             :  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      25             :  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      26             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      27             :  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      28             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      29             :  * OF THE POSSIBILITY OF SUCH DAMAGE.
      30             :  *
      31             :  **********************************************************************/
      32             : 
      33             : #include "krb5_locl.h"
      34             : 
      35             : typedef int PTYPE;
      36             : 
      37             : #ifdef _WIN32
      38             : #include <shlobj.h>
      39             : #include <sddl.h>
      40             : 
      41             : /*
      42             :  * Expand a %{TEMP} token
      43             :  *
      44             :  * The %{TEMP} token expands to the temporary path for the current
      45             :  * user as returned by GetTempPath().
      46             :  *
      47             :  * @note: Since the GetTempPath() function relies on the TMP or TEMP
      48             :  * environment variables, this function will failover to the system
      49             :  * temporary directory until the user profile is loaded.  In addition,
      50             :  * the returned path may or may not exist.
      51             :  */
      52             : static int
      53             : _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
      54             : {
      55             :     TCHAR tpath[MAX_PATH];
      56             :     size_t len;
      57             : 
      58             :     if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
      59             :         if (context)
      60             :             krb5_set_error_message(context, EINVAL,
      61             :                                    "Failed to get temporary path (GLE=%d)",
      62             :                                    GetLastError());
      63             :         return EINVAL;
      64             :     }
      65             : 
      66             :     len = strlen(tpath);
      67             : 
      68             :     if (len > 0 && tpath[len - 1] == '\\')
      69             :         tpath[len - 1] = '\0';
      70             : 
      71             :     *ret = strdup(tpath);
      72             : 
      73             :     if (*ret == NULL) {
      74             :         if (context)
      75             :             krb5_set_error_message(context, ENOMEM, "strdup - Out of memory");
      76             :         return ENOMEM;
      77             :     }
      78             : 
      79             :     return 0;
      80             : }
      81             : 
      82             : extern HINSTANCE _krb5_hInstance;
      83             : 
      84             : /*
      85             :  * Expand a %{BINDIR} token
      86             :  *
      87             :  * This is also used to expand a few other tokens on Windows, since
      88             :  * most of the executable binaries end up in the same directory.  The
      89             :  * "bin" directory is considered to be the directory in which the
      90             :  * krb5.dll is located.
      91             :  */
      92             : static int
      93             : _expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret)
      94             : {
      95             :     TCHAR path[MAX_PATH];
      96             :     TCHAR *lastSlash;
      97             :     DWORD nc;
      98             : 
      99             :     nc = GetModuleFileName(_krb5_hInstance, path, sizeof(path)/sizeof(path[0]));
     100             :     if (nc == 0 ||
     101             :         nc == sizeof(path)/sizeof(path[0])) {
     102             :         return EINVAL;
     103             :     }
     104             : 
     105             :     lastSlash = strrchr(path, '\\');
     106             :     if (lastSlash != NULL) {
     107             :         TCHAR *fslash = strrchr(lastSlash, '/');
     108             : 
     109             :         if (fslash != NULL)
     110             :             lastSlash = fslash;
     111             : 
     112             :         *lastSlash = '\0';
     113             :     }
     114             : 
     115             :     if (postfix) {
     116             :         if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
     117             :             return EINVAL;
     118             :     }
     119             : 
     120             :     *ret = strdup(path);
     121             :     if (*ret == NULL)
     122             :         return ENOMEM;
     123             : 
     124             :     return 0;
     125             : }
     126             : 
     127             : /*
     128             :  *  Expand a %{USERID} token
     129             :  *
     130             :  *  The %{USERID} token expands to the string representation of the
     131             :  *  user's SID.  The user account that will be used is the account
     132             :  *  corresponding to the current thread's security token.  This means
     133             :  *  that:
     134             :  *
     135             :  *  - If the current thread token has the anonymous impersonation
     136             :  *    level, the call will fail.
     137             :  *
     138             :  *  - If the current thread is impersonating a token at
     139             :  *    SecurityIdentification level the call will fail.
     140             :  *
     141             :  */
     142             : static int
     143             : _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **ret)
     144             : {
     145             :     int rv = EINVAL;
     146             :     HANDLE hThread = NULL;
     147             :     HANDLE hToken = NULL;
     148             :     PTOKEN_OWNER pOwner = NULL;
     149             :     DWORD len = 0;
     150             :     LPTSTR strSid = NULL;
     151             : 
     152             :     hThread = GetCurrentThread();
     153             : 
     154             :     if (!OpenThreadToken(hThread, TOKEN_QUERY,
     155             :                          FALSE, /* Open the thread token as the
     156             :                                    current thread user. */
     157             :                          &hToken)) {
     158             : 
     159             :         DWORD le = GetLastError();
     160             : 
     161             :         if (le == ERROR_NO_TOKEN) {
     162             :             HANDLE hProcess = GetCurrentProcess();
     163             : 
     164             :             le = 0;
     165             :             if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
     166             :                 le = GetLastError();
     167             :         }
     168             : 
     169             :         if (le != 0) {
     170             :             if (context)
     171             :                 krb5_set_error_message(context, rv,
     172             :                                        "Can't open thread token (GLE=%d)", le);
     173             :             goto _exit;
     174             :         }
     175             :     }
     176             : 
     177             :     if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
     178             :         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
     179             :             if (context)
     180             :                 krb5_set_error_message(context, rv,
     181             :                                        "Unexpected error reading token information (GLE=%d)",
     182             :                                        GetLastError());
     183             :             goto _exit;
     184             :         }
     185             : 
     186             :         if (len == 0) {
     187             :             if (context)
     188             :                 krb5_set_error_message(context, rv,
     189             :                                       "GetTokenInformation() returned truncated buffer");
     190             :             goto _exit;
     191             :         }
     192             : 
     193             :         pOwner = malloc(len);
     194             :         if (pOwner == NULL) {
     195             :             if (context)
     196             :                 krb5_set_error_message(context, rv, "Out of memory");
     197             :             goto _exit;
     198             :         }
     199             :     } else {
     200             :         if (context)
     201             :             krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
     202             :         goto _exit;
     203             :     }
     204             : 
     205             :     if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
     206             :         if (context)
     207             :             krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError());
     208             :         goto _exit;
     209             :     }
     210             : 
     211             :     if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
     212             :         if (context)
     213             :             krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError());
     214             :         goto _exit;
     215             :     }
     216             : 
     217             :     *ret = strdup(strSid);
     218             :     if (*ret == NULL && context)
     219             :         krb5_set_error_message(context, rv, "Out of memory");
     220             : 
     221             :     rv = 0;
     222             : 
     223             :  _exit:
     224             :     if (hToken != NULL)
     225             :         CloseHandle(hToken);
     226             : 
     227             :     if (pOwner != NULL)
     228             :         free (pOwner);
     229             : 
     230             :     if (strSid != NULL)
     231             :         LocalFree(strSid);
     232             : 
     233             :     return rv;
     234             : }
     235             : 
     236             : /*
     237             :  * Expand a folder identified by a CSIDL
     238             :  */
     239             : 
     240             : static int
     241             : _expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret)
     242             : {
     243             :     TCHAR path[MAX_PATH];
     244             :     size_t len;
     245             : 
     246             :     if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
     247             :         if (context)
     248             :             krb5_set_error_message(context, EINVAL, "Unable to determine folder path");
     249             :         return EINVAL;
     250             :     }
     251             : 
     252             :     len = strlen(path);
     253             : 
     254             :     if (len > 0 && path[len - 1] == '\\')
     255             :         path[len - 1] = '\0';
     256             : 
     257             :     if (postfix &&
     258             :         strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) {
     259             :         return ENOMEM;
     260             :     }
     261             : 
     262             :     *ret = strdup(path);
     263             :     if (*ret == NULL) {
     264             :         if (context)
     265             :             krb5_set_error_message(context, ENOMEM, "Out of memory");
     266             :         return ENOMEM;
     267             :     }
     268             :     return 0;
     269             : }
     270             : 
     271             : #else
     272             : 
     273             : static int
     274           0 : _expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
     275             : {
     276           0 :     *ret = strdup(postfix);
     277           0 :     if (*ret == NULL) {
     278           0 :         krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
     279           0 :         return ENOMEM;
     280             :     }
     281           0 :     return 0;
     282             : }
     283             : 
     284             : static int
     285           0 : _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
     286             : {
     287           0 :     const char *p = NULL;
     288             : 
     289           0 :     if (issuid())
     290           0 :         p = getenv("TEMP");
     291           0 :     if (p)
     292           0 :         *ret = strdup(p);
     293             :     else
     294           0 :         *ret = strdup("/tmp");
     295           0 :     if (*ret == NULL)
     296           0 :         return ENOMEM;
     297           0 :     return 0;
     298             : }
     299             : 
     300             : static int
     301           1 : _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)
     302             : {
     303           1 :     int ret = asprintf(str, "%ld", (unsigned long)getuid());
     304           1 :     if (ret < 0 || *str == NULL)
     305           0 :         return ENOMEM;
     306           1 :     return 0;
     307             : }
     308             : 
     309             : 
     310             : #endif /* _WIN32 */
     311             : 
     312             : /**
     313             :  * Expand a %{null} token
     314             :  *
     315             :  * The expansion of a %{null} token is always the empty string.
     316             :  */
     317             : 
     318             : static int
     319           0 : _expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
     320             : {
     321           0 :     *ret = strdup("");
     322           0 :     if (*ret == NULL) {
     323           0 :         if (context)
     324           0 :             krb5_set_error_message(context, ENOMEM, "Out of memory");
     325           0 :         return ENOMEM;
     326             :     }
     327           0 :     return 0;
     328             : }
     329             : 
     330             : 
     331             : static const struct {
     332             :     const char * tok;
     333             :     int ftype;
     334             : #define FTYPE_CSIDL 0
     335             : #define FTYPE_SPECIAL 1
     336             : 
     337             :     PTYPE param;
     338             :     const char * postfix;
     339             : 
     340             :     int (*exp_func)(krb5_context, PTYPE, const char *, char **);
     341             : 
     342             : #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
     343             : #define SPECIAL(f) SPECIALP(f, NULL)
     344             : 
     345             : } tokens[] = {
     346             : #ifdef _WIN32
     347             : #define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
     348             : #define CSIDL(C) CSIDLP(C, NULL)
     349             : 
     350             :     {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
     351             :     {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
     352             :     {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
     353             :     {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
     354             :     {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
     355             :     {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
     356             :     {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
     357             :     {"LIBDIR", SPECIAL(_expand_bin_dir)},
     358             :     {"BINDIR", SPECIAL(_expand_bin_dir)},
     359             :     {"LIBEXEC", SPECIAL(_expand_bin_dir)},
     360             :     {"SBINDIR", SPECIAL(_expand_bin_dir)},
     361             : #else
     362             :     {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, _expand_path},
     363             :     {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, _expand_path},
     364             :     {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, _expand_path},
     365             :     {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, _expand_path},
     366             : #endif
     367             :     {"TEMP", SPECIAL(_expand_temp_folder)},
     368             :     {"USERID", SPECIAL(_expand_userid)},
     369             :     {"uid", SPECIAL(_expand_userid)},
     370             :     {"null", SPECIAL(_expand_null)}
     371             : };
     372             : 
     373             : static int
     374           1 : _expand_token(krb5_context context,
     375             :               const char *token,
     376             :               const char *token_end,
     377             :               char **ret)
     378             : {
     379             :     size_t i;
     380             : 
     381           1 :     *ret = NULL;
     382             : 
     383           2 :     if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
     384           1 :         token_end - token <= 2) {
     385           0 :         if (context)
     386           0 :             krb5_set_error_message(context, EINVAL,"Invalid token.");
     387           0 :         return EINVAL;
     388             :     }
     389             : 
     390           7 :     for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
     391           7 :         if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))
     392           1 :             return tokens[i].exp_func(context, tokens[i].param,
     393             :                                       tokens[i].postfix, ret);
     394             :     }
     395             : 
     396           0 :     if (context)
     397           0 :         krb5_set_error_message(context, EINVAL, "Invalid token.");
     398           0 :     return EINVAL;
     399             : }
     400             : 
     401             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     402      265404 : _krb5_expand_path_tokens(krb5_context context,
     403             :                          const char *path_in,
     404             :                          char **ppath_out)
     405             : {
     406             :     char *tok_begin, *tok_end, *append;
     407             :     const char *path_left;
     408      265404 :     size_t len = 0;
     409             : 
     410      265404 :     if (path_in == NULL || *path_in == '\0') {
     411           0 :         *ppath_out = strdup("");
     412           0 :         return 0;
     413             :     }
     414             : 
     415      265404 :     *ppath_out = NULL;
     416             : 
     417      796213 :     for (path_left = path_in; path_left && *path_left; ) {
     418             : 
     419      265405 :         tok_begin = strstr(path_left, "%{");
     420             : 
     421      265405 :         if (tok_begin && tok_begin != path_left) {
     422             : 
     423           1 :             append = malloc((tok_begin - path_left) + 1);
     424           1 :             if (append) {
     425           1 :                 memcpy(append, path_left, tok_begin - path_left);
     426           1 :                 append[tok_begin - path_left] = '\0';
     427             :             }
     428           1 :             path_left = tok_begin;
     429             : 
     430      265404 :         } else if (tok_begin) {
     431             : 
     432           1 :             tok_end = strchr(tok_begin, '}');
     433           1 :             if (tok_end == NULL) {
     434           0 :                 if (*ppath_out)
     435           0 :                     free(*ppath_out);
     436           0 :                 *ppath_out = NULL;
     437           0 :                 if (context)
     438           0 :                     krb5_set_error_message(context, EINVAL, "variable missing }");
     439           0 :                 return EINVAL;
     440             :             }
     441             : 
     442           1 :             if (_expand_token(context, tok_begin, tok_end, &append)) {
     443           0 :                 if (*ppath_out)
     444           0 :                     free(*ppath_out);
     445           0 :                 *ppath_out = NULL;
     446           0 :                 return EINVAL;
     447             :             }
     448             : 
     449           1 :             path_left = tok_end + 1;
     450             :         } else {
     451             : 
     452      265403 :             append = strdup(path_left);
     453      265403 :             path_left = NULL;
     454             : 
     455             :         }
     456             : 
     457      265405 :         if (append == NULL) {
     458             : 
     459           0 :             if (*ppath_out)
     460           0 :                 free(*ppath_out);
     461           0 :             *ppath_out = NULL;
     462           0 :             if (context)
     463           0 :                 krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
     464           0 :             return ENOMEM;
     465             : 
     466             :         }
     467             : 
     468             :         {
     469      265405 :             size_t append_len = strlen(append);
     470      265405 :             char * new_str = realloc(*ppath_out, len + append_len + 1);
     471             : 
     472      265405 :             if (new_str == NULL) {
     473           0 :                 free(append);
     474           0 :                 if (*ppath_out)
     475           0 :                     free(*ppath_out);
     476           0 :                 *ppath_out = NULL;
     477           0 :                 if (context)
     478           0 :                     krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
     479           0 :                 return ENOMEM;
     480             :             }
     481             : 
     482      265405 :             *ppath_out = new_str;
     483      265836 :             memcpy(*ppath_out + len, append, append_len + 1);
     484      265405 :             len = len + append_len;
     485      265405 :             free(append);
     486             :         }
     487             :     }
     488             : 
     489             : #ifdef _WIN32
     490             :     /* Also deal with slashes */
     491             :     if (*ppath_out) {
     492             :         char * c;
     493             :         for (c = *ppath_out; *c; c++)
     494             :             if (*c == '/')
     495             :                 *c = '\\';
     496             :     }
     497             : #endif
     498             : 
     499      264973 :     return 0;
     500             : }

Generated by: LCOV version 1.13