components/krb5/patches/061-ccache-nounlink.patch
changeset 5986 bab15c34f645
parent 5490 9bf0bc57423a
equal deleted inserted replaced
5985:6b195cad32d4 5986:bab15c34f645
       
     1 #
       
     2 # This patch modifies the MIT implementation of krb5_fcc_initialize() so
       
     3 # it doesn't call unlink() on an existing ccache file.  This modification
       
     4 # was done a long time ago in Solaris to workaround a race condition
       
     5 # brought on by the interaction between Solaris pam_krb5 and MIT's
       
     6 # implementation of krb5_fcc_initialize().  Given there are better ways of
       
     7 # fixing the race condition we will not give this patch to MIT however a
       
     8 # proper race condition fix would take prohibitively long to implement
       
     9 # hence this patch.  When pam_krb5 is modified to better deal with the
       
    10 # ccache file and RFE 22229031 regarding ktkt_warnd is implemented then
       
    11 # this patch can be removed.
       
    12 # Patch source: in-house
       
    13 #
       
    14 
       
    15 diff -Naru old/src/lib/krb5/ccache/cc_file.c new/src/lib/krb5/ccache/cc_file.c
       
    16 --- old/src/lib/krb5/ccache/cc_file.c	2015-05-08 16:27:02.000000000 -0700
       
    17 +++ new/src/lib/krb5/ccache/cc_file.c	2015-11-16 15:54:02.138183303 -0800
       
    18 @@ -64,6 +64,10 @@
       
    19  #include "k5-int.h"
       
    20  #include "cc-int.h"
       
    21  
       
    22 +/* Solaris Kerberos */
       
    23 +#include <syslog.h>
       
    24 +#include <ctype.h>
       
    25 +
       
    26  #include <stdio.h>
       
    27  #include <errno.h>
       
    28  
       
    29 @@ -71,6 +75,11 @@
       
    30  #include <unistd.h>
       
    31  #endif
       
    32  
       
    33 +/* Solaris Kerberos */
       
    34 +/* How long to block if flock fails with EAGAIN */
       
    35 +#define    LOCK_RETRIES    100
       
    36 +#define    WAIT_LENGTH    20    /* in milliseconds */
       
    37 +
       
    38  extern const krb5_cc_ops krb5_cc_file_ops;
       
    39  
       
    40  krb5_error_code krb5_change_cache(void);
       
    41 @@ -85,6 +94,7 @@
       
    42  #define FCC_OPEN_AND_ERASE      1
       
    43  #define FCC_OPEN_RDWR           2
       
    44  #define FCC_OPEN_RDONLY         3
       
    45 +#define	FCC_OPEN_AND_ERASE_NOUNLINK	255    /* Solaris Kerberos */
       
    46  
       
    47  #define FCC_TAG_DELTATIME       1
       
    48  
       
    49 @@ -524,6 +534,130 @@
       
    50      ((SIZE) < BUFSIZE ? (abort(),0) : setbuf(FILE, BUF))
       
    51  #endif
       
    52  
       
    53 +/* Solaris Kerberos */
       
    54 +static krb5_error_code
       
    55 +krb5_fcc_open_nounlink(char *filename, int open_flag, int *ret_fd, int *new)
       
    56 +{
       
    57 +     struct stat lres;
       
    58 +     struct stat fres;
       
    59 +     int error;
       
    60 +     uid_t uid, euid;
       
    61 +     int fd;
       
    62 +     int newfile = 0;
       
    63 +
       
    64 +     *ret_fd = -1;
       
    65 +     /*
       
    66 +      * Solaris Kerberos
       
    67 +      * If we are opening in NOUNLINK mode, we have to check that the
       
    68 +      * existing file, if any, is not a symlink. If it is, we try to
       
    69 +      * delete and re-create it.
       
    70 +      */
       
    71 +     error = lstat(filename, &lres);
       
    72 +     if (error == -1 && errno != ENOENT) {
       
    73 +         syslog(LOG_ERR, "lstat failed for %s [%m]", filename);
       
    74 +         return (-1);
       
    75 +     }
       
    76 +
       
    77 +     if (error == 0 && !S_ISREG(lres.st_mode)) {
       
    78 +         syslog(LOG_WARNING, "%s is not a plain file!", filename);
       
    79 +         syslog(LOG_WARNING, "trying to unlink %s", filename);
       
    80 +         if (unlink(filename) != 0) {
       
    81 +              syslog(LOG_ERR, "could not unlink %s [%m]", filename);
       
    82 +              return (-1);
       
    83 +         }
       
    84 +     }
       
    85 +
       
    86 +     fd = THREEPARAMOPEN(filename, open_flag | O_NONBLOCK | O_NOFOLLOW, 0600);
       
    87 +     if (fd == -1) {
       
    88 +         if (errno == ENOENT) {
       
    89 +              fd = THREEPARAMOPEN(filename, open_flag | O_EXCL | O_CREAT,
       
    90 +                  0600);
       
    91 +              if (fd != -1) {
       
    92 +                  newfile = 1;
       
    93 +              } else {
       
    94 +                  /* If the file got created after the open we must retry */
       
    95 +                  if (errno == EEXIST)
       
    96 +                      return (0);
       
    97 +              }
       
    98 +         } else if (errno == EACCES) {
       
    99 +            /*
       
   100 +             * We failed since the file existed with wrong permissions.
       
   101 +             * Let's try to unlink it and if that succeeds retry.
       
   102 +             */
       
   103 +            syslog(LOG_WARNING, "Insufficient permissions on %s", filename);
       
   104 +            syslog(LOG_WARNING, "trying to unlink %s", filename);
       
   105 +            if (unlink(filename) != 0) {
       
   106 +                syslog(LOG_ERR, "could not unlink %s [%m]", filename);
       
   107 +                return (-1);
       
   108 +            }
       
   109 +            return (0);
       
   110 +        }
       
   111 +    }
       
   112 +    /* If we still don't have a valid fd, we stop trying */
       
   113 +    if (fd == -1)
       
   114 +        return (-1);
       
   115 +
       
   116 +    /*
       
   117 +     * Solaris Kerberos
       
   118 +     * If the file was not created now with a O_CREAT | O_EXCL open,
       
   119 +     * we have opened an existing file. We should check if the file
       
   120 +     * owner is us, if not, unlink and retry. If unlink fails we log
       
   121 +     * the error and return.
       
   122 +     */
       
   123 +    if (!newfile) {
       
   124 +        if (fstat(fd, &fres) == -1) {
       
   125 +            syslog(LOG_ERR, "lstat failed for %s [%m]", filename);
       
   126 +            close(fd);
       
   127 +            return (-1);
       
   128 +        }
       
   129 +        /* Check if this is the same file we lstat'd earlier */
       
   130 +        if (lres.st_dev != fres.st_dev || lres.st_ino != fres.st_ino) {
       
   131 +            syslog(LOG_ERR, "%s changed between stat and open!", filename);
       
   132 +            close(fd);
       
   133 +            return (-1);
       
   134 +        }
       
   135 +
       
   136 +        /*
       
   137 +         * Solaris Kerberos
       
   138 +         * Check if the cc filename uid matches owner of file.
       
   139 +         * Expects cc file to be in the form of /tmp/krb5cc_<uid>,
       
   140 +         * else skip this check.
       
   141 +         */
       
   142 +        if (strncmp(filename, "/tmp/krb5cc_", strlen("/tmp/krb5cc_")) == 0) {
       
   143 +            uid_t fname_uid;
       
   144 +            char *uidstr = strchr(filename, '_');
       
   145 +            char *s = NULL;
       
   146 +
       
   147 +            /* make sure we have some non-null char after '_' */
       
   148 +            if (!*++uidstr)
       
   149 +                goto out;
       
   150 +
       
   151 +            /* make sure the uid part is all digits */
       
   152 +            for (s = uidstr; *s; s++)
       
   153 +                if (!isdigit(*s))
       
   154 +                    goto out;
       
   155 +
       
   156 +            fname_uid = (uid_t) atoi(uidstr);
       
   157 +            if (fname_uid != fres.st_uid) {
       
   158 +                close(fd);
       
   159 +                syslog(LOG_WARNING, "%s owned by %d instead of %d",
       
   160 +                    filename, fres.st_uid, fname_uid);
       
   161 +                syslog(LOG_WARNING, "trying to unlink %s", filename);
       
   162 +                if (unlink(filename) != 0) {
       
   163 +                    syslog(LOG_ERR, "could not unlink %s [%m]", filename);
       
   164 +                    return (-1);
       
   165 +                }
       
   166 +                return (0);
       
   167 +            }
       
   168 +        }
       
   169 +    }
       
   170 +
       
   171 +out:
       
   172 +    *new = newfile;
       
   173 +    *ret_fd = fd;
       
   174 +    return (0);
       
   175 +}
       
   176 +
       
   177  /* Open and lock the cache file.  If mode is FCC_OPEN_AND_ERASE, initialize it
       
   178   * with a header.  Call with the mutex locked. */
       
   179  static krb5_error_code
       
   180 @@ -538,6 +672,10 @@
       
   181      int f, open_flag, lock_flag, cnt;
       
   182      char buf[1024];
       
   183  
       
   184 +    /* Solaris Kerberos */
       
   185 +    int retries = 0;
       
   186 +    int newfile = 0;
       
   187 +
       
   188      k5_cc_mutex_assert_locked(context, &data->lock);
       
   189      invalidate_cache(data);
       
   190  
       
   191 @@ -549,6 +687,10 @@
       
   192      }
       
   193  
       
   194      switch (mode) {
       
   195 +	/* Solaris Kerberos */
       
   196 +    case FCC_OPEN_AND_ERASE_NOUNLINK:
       
   197 +        open_flag = O_RDWR;
       
   198 +        break;
       
   199      case FCC_OPEN_AND_ERASE:
       
   200          unlink(data->filename);
       
   201          open_flag = O_CREAT | O_EXCL | O_TRUNC | O_RDWR;
       
   202 @@ -562,7 +704,21 @@
       
   203          break;
       
   204      }
       
   205  
       
   206 +fcc_retry:
       
   207 +    /*
       
   208 +     * Solaris Kerberos
       
   209 +     * If we are opening in NOUNLINK mode, check whether we are opening a
       
   210 +     * symlink or a file owned by some other user and take preventive action.
       
   211 +     */
       
   212 +    newfile = 0;
       
   213 +    if (mode == FCC_OPEN_AND_ERASE_NOUNLINK) {
       
   214 +     ret = krb5_fcc_open_nounlink(data->filename, open_flag,
       
   215 +                     &f, &newfile);
       
   216 +     if (ret == 0 && f == -1)
       
   217 +          goto fcc_retry;
       
   218 +    } else {
       
   219      f = THREEPARAMOPEN(data->filename, open_flag | O_BINARY, 0600);
       
   220 +    }
       
   221      if (f == NO_FILE) {
       
   222          if (errno == ENOENT) {
       
   223              ret = KRB5_FCC_NOFILE;
       
   224 @@ -584,10 +740,26 @@
       
   225      ret = krb5_lock_file(context, f, lock_flag);
       
   226      if (ret) {
       
   227          (void)close(f);
       
   228 +        if (ret == EAGAIN && retries++ < LOCK_RETRIES) {
       
   229 +            /* Solaris Kerberos wait some time before retrying */
       
   230 +            if (poll(NULL, 0, WAIT_LENGTH) == 0)
       
   231 +                goto fcc_retry;
       
   232 +        }
       
   233 +        syslog(LOG_ERR, "Failed to lock %s [%m]", data->filename);
       
   234          return ret;
       
   235      }
       
   236  
       
   237 -    if (mode == FCC_OPEN_AND_ERASE) {
       
   238 +    if (mode == FCC_OPEN_AND_ERASE || mode == FCC_OPEN_AND_ERASE_NOUNLINK) {
       
   239 +        /*
       
   240 +         * Solaris Kerberos
       
   241 +         * If this file was not created, we have to flush existing data.
       
   242 +         * This will happen only if we are doing an ERASE_NOUNLINK open.
       
   243 +         */
       
   244 +        if (newfile == 0 && (ftruncate(f, 0) == -1)) {
       
   245 +            syslog(LOG_ERR, "ftruncate failed for %s [%m]", data->filename);
       
   246 +            close(f);
       
   247 +            return (interpret_errno(context, errno));
       
   248 +        }
       
   249          /* write the version number */
       
   250          store_16_be(context->fcc_default_format, fcc_fvno);
       
   251          data->version = context->fcc_default_format;
       
   252 @@ -755,14 +927,16 @@
       
   253  
       
   254      k5_cc_mutex_lock(context, &data->lock);
       
   255  
       
   256 -    MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE);
       
   257 +    MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE_NOUNLINK);
       
   258  
       
   259 +#if 0
       
   260  #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
       
   261  #ifdef HAVE_FCHMOD
       
   262      st = fchmod(data->fd, S_IRUSR | S_IWUSR);
       
   263  #else
       
   264      st = chmod(data->filename, S_IRUSR | S_IWUSR);
       
   265  #endif
       
   266 +#endif
       
   267      if (st == -1) {
       
   268          ret = interpret_errno(context, errno);
       
   269          MAYBE_CLOSE(context, id, ret);