Coverage Report

Created: 2025-10-28 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/plugins/sudoers/match_digest.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 1996, 1998-2005, 2007-2020
5
 *  Todd C. Miller <Todd.Miller@sudo.ws>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 *
19
 * Sponsored in part by the Defense Advanced Research Projects
20
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22
 */
23
24
#include <config.h>
25
26
#include <errno.h>
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <fcntl.h>
31
#include <unistd.h>
32
33
#include <sudoers.h>
34
#include <sudo_digest.h>
35
#include <gram.h>
36
37
int
38
digest_matches(int fd, const char *path, const char *runchroot,
39
    const struct command_digest_list *digests)
40
0
{
41
0
    unsigned int digest_type = SUDO_DIGEST_INVALID;
42
0
    unsigned char *file_digest = NULL;
43
0
    unsigned char *sudoers_digest = NULL;
44
0
    struct command_digest *digest;
45
0
    char pathbuf[PATH_MAX];
46
0
    size_t digest_len;
47
0
    int matched = DENY;
48
0
    int fd2 = -1;
49
0
    debug_decl(digest_matches, SUDOERS_DEBUG_MATCH);
50
51
0
    if (TAILQ_EMPTY(digests)) {
52
  /* No digest, no problem. */
53
0
  debug_return_int(ALLOW);
54
0
    }
55
56
0
    if (runchroot != NULL) {
57
        /* XXX - handle symlinks and '..' in path outside chroot */
58
0
        const int len =
59
0
            snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path);
60
0
        if (len >= ssizeof(pathbuf)) {
61
0
            errno = ENAMETOOLONG;
62
0
            sudo_warn("%s%s", runchroot, path);
63
0
            goto done;
64
0
        }
65
0
        path = pathbuf;
66
0
    }
67
68
0
    if (fd == -1) {
69
0
        fd2 = open(path, O_RDONLY|O_NONBLOCK);
70
0
        if (fd2 == -1) {
71
            /* No file, no match. */
72
0
            goto done;
73
0
        }
74
0
        fd = fd2;
75
0
    }
76
77
0
    TAILQ_FOREACH(digest, digests, entries) {
78
  /* Compute file digest if needed. */
79
0
  if (digest->digest_type != digest_type) {
80
0
      free(file_digest);
81
0
      file_digest = sudo_filedigest(fd, path, digest->digest_type,
82
0
    &digest_len);
83
0
      if (lseek(fd, (off_t)0, SEEK_SET) == -1) {
84
0
    sudo_debug_printf(
85
0
        SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
86
0
        "unable to rewind digest fd");
87
0
      }
88
0
      digest_type = digest->digest_type;
89
0
  }
90
0
  if (file_digest == NULL) {
91
      /* Warning (if any) printed by sudo_filedigest() */
92
0
      goto done;
93
0
  }
94
95
  /* Convert the command digest from ascii to binary. */
96
0
  sudoers_digest = malloc(digest_len); // -V614
97
0
  if (sudoers_digest == NULL) {
98
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
99
0
      goto done;
100
0
  }
101
0
  if (strlen(digest->digest_str) == digest_len * 2) {
102
      /* Convert ascii hex to binary. */
103
0
      size_t i;
104
0
      for (i = 0; i < digest_len; i++) {
105
0
    const int h = sudo_hexchar(&digest->digest_str[2 * i]);
106
0
    if (h == -1)
107
0
        goto bad_format;
108
0
    sudoers_digest[i] = (unsigned char)h;
109
0
      }
110
0
  } else {
111
      /* Convert base64 to binary. */
112
0
      size_t len = sudo_base64_decode(digest->digest_str, sudoers_digest,
113
0
    digest_len);
114
0
      if (len == (size_t)-1)
115
0
    goto bad_format;
116
0
      if (len != digest_len) {
117
0
    sudo_warnx(
118
0
        U_("digest for %s (%s) bad length %zu, expected %zu"),
119
0
        path, digest->digest_str, len, digest_len);
120
0
    goto done;
121
0
      }
122
0
  }
123
0
  if (memcmp(file_digest, sudoers_digest, digest_len) == 0) {
124
0
      matched = ALLOW;
125
0
      break;
126
0
  }
127
128
0
  sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
129
0
      "%s digest mismatch for %s, expecting %s",
130
0
      digest_type_to_name(digest->digest_type), path, digest->digest_str);
131
0
  free(sudoers_digest);
132
0
  sudoers_digest = NULL;
133
0
    }
134
0
    goto done;
135
136
0
bad_format:
137
0
    sudo_warnx(U_("digest for %s (%s) is not in %s form"), path,
138
0
  digest->digest_str, digest_type_to_name(digest->digest_type));
139
0
done:
140
0
    if (fd2 != -1)
141
0
  close(fd2);
142
0
    free(sudoers_digest);
143
0
    free(file_digest);
144
0
    debug_return_int(matched);
145
0
}