refs.con commit git-check-ref-format: reject funny ref names. (03feddd)
   1#include "refs.h"
   2#include "cache.h"
   3
   4#include <errno.h>
   5
   6/* We allow "recursive" symbolic refs. Only within reason, though */
   7#define MAXDEPTH 5
   8
   9#ifndef USE_SYMLINK_HEAD
  10#define USE_SYMLINK_HEAD 1
  11#endif
  12
  13int validate_symref(const char *path)
  14{
  15        struct stat st;
  16        char *buf, buffer[256];
  17        int len, fd;
  18
  19        if (lstat(path, &st) < 0)
  20                return -1;
  21
  22        /* Make sure it is a "refs/.." symlink */
  23        if (S_ISLNK(st.st_mode)) {
  24                len = readlink(path, buffer, sizeof(buffer)-1);
  25                if (len >= 5 && !memcmp("refs/", buffer, 5))
  26                        return 0;
  27                return -1;
  28        }
  29
  30        /*
  31         * Anything else, just open it and try to see if it is a symbolic ref.
  32         */
  33        fd = open(path, O_RDONLY);
  34        if (fd < 0)
  35                return -1;
  36        len = read(fd, buffer, sizeof(buffer)-1);
  37        close(fd);
  38
  39        /*
  40         * Is it a symbolic ref?
  41         */
  42        if (len < 4 || memcmp("ref:", buffer, 4))
  43                return -1;
  44        buf = buffer + 4;
  45        len -= 4;
  46        while (len && isspace(*buf))
  47                buf++, len--;
  48        if (len >= 5 && !memcmp("refs/", buf, 5))
  49                return 0;
  50        return -1;
  51}
  52
  53const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
  54{
  55        int depth = MAXDEPTH, len;
  56        char buffer[256];
  57
  58        for (;;) {
  59                struct stat st;
  60                char *buf;
  61                int fd;
  62
  63                if (--depth < 0)
  64                        return NULL;
  65
  66                /* Special case: non-existing file.
  67                 * Not having the refs/heads/new-branch is OK
  68                 * if we are writing into it, so is .git/HEAD
  69                 * that points at refs/heads/master still to be
  70                 * born.  It is NOT OK if we are resolving for
  71                 * reading.
  72                 */
  73                if (lstat(path, &st) < 0) {
  74                        if (reading || errno != ENOENT)
  75                                return NULL;
  76                        memset(sha1, 0, 20);
  77                        return path;
  78                }
  79
  80                /* Follow "normalized" - ie "refs/.." symlinks by hand */
  81                if (S_ISLNK(st.st_mode)) {
  82                        len = readlink(path, buffer, sizeof(buffer)-1);
  83                        if (len >= 5 && !memcmp("refs/", buffer, 5)) {
  84                                path = git_path("%.*s", len, buffer);
  85                                continue;
  86                        }
  87                }
  88
  89                /*
  90                 * Anything else, just open it and try to use it as
  91                 * a ref
  92                 */
  93                fd = open(path, O_RDONLY);
  94                if (fd < 0)
  95                        return NULL;
  96                len = read(fd, buffer, sizeof(buffer)-1);
  97                close(fd);
  98
  99                /*
 100                 * Is it a symbolic ref?
 101                 */
 102                if (len < 4 || memcmp("ref:", buffer, 4))
 103                        break;
 104                buf = buffer + 4;
 105                len -= 4;
 106                while (len && isspace(*buf))
 107                        buf++, len--;
 108                while (len && isspace(buf[len-1]))
 109                        buf[--len] = 0;
 110                path = git_path("%.*s", len, buf);
 111        }
 112        if (len < 40 || get_sha1_hex(buffer, sha1))
 113                return NULL;
 114        return path;
 115}
 116
 117int create_symref(const char *git_HEAD, const char *refs_heads_master)
 118{
 119#if USE_SYMLINK_HEAD
 120        unlink(git_HEAD);
 121        return symlink(refs_heads_master, git_HEAD);
 122#else
 123        const char *lockpath;
 124        char ref[1000];
 125        int fd, len, written;
 126
 127        len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
 128        if (sizeof(ref) <= len) {
 129                error("refname too long: %s", refs_heads_master);
 130                return -1;
 131        }
 132        lockpath = mkpath("%s.lock", git_HEAD);
 133        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); 
 134        written = write(fd, ref, len);
 135        close(fd);
 136        if (written != len) {
 137                unlink(lockpath);
 138                error("Unable to write to %s", lockpath);
 139                return -2;
 140        }
 141        if (rename(lockpath, git_HEAD) < 0) {
 142                unlink(lockpath);
 143                error("Unable to create %s", git_HEAD);
 144                return -3;
 145        }
 146        return 0;
 147#endif
 148}
 149
 150int read_ref(const char *filename, unsigned char *sha1)
 151{
 152        if (resolve_ref(filename, sha1, 1))
 153                return 0;
 154        return -1;
 155}
 156
 157static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1))
 158{
 159        int retval = 0;
 160        DIR *dir = opendir(git_path("%s", base));
 161
 162        if (dir) {
 163                struct dirent *de;
 164                int baselen = strlen(base);
 165                char *path = xmalloc(baselen + 257);
 166
 167                if (!strncmp(base, "./", 2)) {
 168                        base += 2;
 169                        baselen -= 2;
 170                }
 171                memcpy(path, base, baselen);
 172                if (baselen && base[baselen-1] != '/')
 173                        path[baselen++] = '/';
 174
 175                while ((de = readdir(dir)) != NULL) {
 176                        unsigned char sha1[20];
 177                        struct stat st;
 178                        int namelen;
 179
 180                        if (de->d_name[0] == '.')
 181                                continue;
 182                        namelen = strlen(de->d_name);
 183                        if (namelen > 255)
 184                                continue;
 185                        memcpy(path + baselen, de->d_name, namelen+1);
 186                        if (stat(git_path("%s", path), &st) < 0)
 187                                continue;
 188                        if (S_ISDIR(st.st_mode)) {
 189                                retval = do_for_each_ref(path, fn);
 190                                if (retval)
 191                                        break;
 192                                continue;
 193                        }
 194                        if (read_ref(git_path("%s", path), sha1) < 0)
 195                                continue;
 196                        if (!has_sha1_file(sha1))
 197                                continue;
 198                        retval = fn(path, sha1);
 199                        if (retval)
 200                                break;
 201                }
 202                free(path);
 203                closedir(dir);
 204        }
 205        return retval;
 206}
 207
 208int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
 209{
 210        unsigned char sha1[20];
 211        if (!read_ref(git_path("HEAD"), sha1))
 212                return fn("HEAD", sha1);
 213        return 0;
 214}
 215
 216int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
 217{
 218        return do_for_each_ref("refs", fn);
 219}
 220
 221static char *ref_file_name(const char *ref)
 222{
 223        char *base = get_refs_directory();
 224        int baselen = strlen(base);
 225        int reflen = strlen(ref);
 226        char *ret = xmalloc(baselen + 2 + reflen);
 227        sprintf(ret, "%s/%s", base, ref);
 228        return ret;
 229}
 230
 231static char *ref_lock_file_name(const char *ref)
 232{
 233        char *base = get_refs_directory();
 234        int baselen = strlen(base);
 235        int reflen = strlen(ref);
 236        char *ret = xmalloc(baselen + 7 + reflen);
 237        sprintf(ret, "%s/%s.lock", base, ref);
 238        return ret;
 239}
 240
 241int get_ref_sha1(const char *ref, unsigned char *sha1)
 242{
 243        const char *filename;
 244
 245        if (check_ref_format(ref))
 246                return -1;
 247        filename = git_path("refs/%s", ref);
 248        return read_ref(filename, sha1);
 249}
 250
 251static int lock_ref_file(const char *filename, const char *lock_filename,
 252                         const unsigned char *old_sha1)
 253{
 254        int fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 255        unsigned char current_sha1[20];
 256        int retval;
 257        if (fd < 0) {
 258                return error("Couldn't open lock file for %s: %s",
 259                             filename, strerror(errno));
 260        }
 261        retval = read_ref(filename, current_sha1);
 262        if (old_sha1) {
 263                if (retval) {
 264                        close(fd);
 265                        unlink(lock_filename);
 266                        return error("Could not read the current value of %s",
 267                                     filename);
 268                }
 269                if (memcmp(current_sha1, old_sha1, 20)) {
 270                        close(fd);
 271                        unlink(lock_filename);
 272                        error("The current value of %s is %s",
 273                              filename, sha1_to_hex(current_sha1));
 274                        return error("Expected %s",
 275                                     sha1_to_hex(old_sha1));
 276                }
 277        } else {
 278                if (!retval) {
 279                        close(fd);
 280                        unlink(lock_filename);
 281                        return error("Unexpectedly found a value of %s for %s",
 282                                     sha1_to_hex(current_sha1), filename);
 283                }
 284        }
 285        return fd;
 286}
 287
 288int lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
 289{
 290        char *filename;
 291        char *lock_filename;
 292        int retval;
 293        if (check_ref_format(ref))
 294                return -1;
 295        filename = ref_file_name(ref);
 296        lock_filename = ref_lock_file_name(ref);
 297        retval = lock_ref_file(filename, lock_filename, old_sha1);
 298        free(filename);
 299        free(lock_filename);
 300        return retval;
 301}
 302
 303static int write_ref_file(const char *filename,
 304                          const char *lock_filename, int fd,
 305                          const unsigned char *sha1)
 306{
 307        char *hex = sha1_to_hex(sha1);
 308        char term = '\n';
 309        if (write(fd, hex, 40) < 40 ||
 310            write(fd, &term, 1) < 1) {
 311                error("Couldn't write %s\n", filename);
 312                close(fd);
 313                return -1;
 314        }
 315        close(fd);
 316        rename(lock_filename, filename);
 317        return 0;
 318}
 319
 320int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
 321{
 322        char *filename;
 323        char *lock_filename;
 324        int retval;
 325        if (fd < 0)
 326                return -1;
 327        if (check_ref_format(ref))
 328                return -1;
 329        filename = ref_file_name(ref);
 330        lock_filename = ref_lock_file_name(ref);
 331        retval = write_ref_file(filename, lock_filename, fd, sha1);
 332        free(filename);
 333        free(lock_filename);
 334        return retval;
 335}
 336
 337/*
 338 * Make sure "ref" is something reasonable to have under ".git/refs/";
 339 * We do not like it if:
 340 *
 341 * - any path component of it begins with ".", or
 342 * - it has double dots "..", or
 343 * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
 344 * - it ends with a "/".
 345 */
 346
 347static inline int bad_ref_char(int ch)
 348{
 349        return (((unsigned) ch) <= ' ' ||
 350                ch == '~' || ch == '^' || ch == ':');
 351}
 352
 353int check_ref_format(const char *ref)
 354{
 355        int ch, level;
 356        const char *cp = ref;
 357
 358        level = 0;
 359        while (1) {
 360                while ((ch = *cp++) == '/')
 361                        ; /* tolerate duplicated slashes */
 362                if (!ch)
 363                        return -1; /* should not end with slashes */
 364
 365                /* we are at the beginning of the path component */
 366                if (ch == '.' || bad_ref_char(ch))
 367                        return -1;
 368
 369                /* scan the rest of the path component */
 370                while ((ch = *cp++) != 0) {
 371                        if (bad_ref_char(ch))
 372                                return -1;
 373                        if (ch == '/')
 374                                break;
 375                        if (ch == '.' && *cp == '.')
 376                                return -1;
 377                }
 378                level++;
 379                if (!ch) {
 380                        if (level < 2)
 381                                return -1; /* at least of form "heads/blah" */
 382                        return 0;
 383                }
 384        }
 385}
 386
 387int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
 388{
 389        char *filename;
 390        char *lock_filename;
 391        int fd;
 392        int retval;
 393        if (check_ref_format(ref))
 394                return -1;
 395        filename = ref_file_name(ref);
 396        lock_filename = ref_lock_file_name(ref);
 397        fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 398        if (fd < 0) {
 399                error("Writing %s", lock_filename);
 400                perror("Open");
 401        }
 402        retval = write_ref_file(filename, lock_filename, fd, sha1);
 403        free(filename);
 404        free(lock_filename);
 405        return retval;
 406}