Coverage Report

Created: 2025-10-13 06:08

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
47
{
41
47
    unsigned int digest_type = SUDO_DIGEST_INVALID;
42
47
    unsigned char *file_digest = NULL;
43
47
    unsigned char *sudoers_digest = NULL;
44
47
    struct command_digest *digest;
45
47
    char pathbuf[PATH_MAX];
46
47
    size_t digest_len;
47
47
    int matched = DENY;
48
47
    int fd2 = -1;
49
47
    debug_decl(digest_matches, SUDOERS_DEBUG_MATCH);
50
51
47
    if (TAILQ_EMPTY(digests)) {
52
  /* No digest, no problem. */
53
34
  debug_return_int(ALLOW);
54
34
    }
55
56
13
    if (fd == -1) {
57
2
  fd2 = open(path, O_RDONLY|O_NONBLOCK);
58
2
  if (fd2 == -1) {
59
      /* No file, no match. */
60
0
      goto done;
61
0
  }
62
2
  fd = fd2;
63
2
    }
64
65
13
    if (runchroot != NULL) {
66
  /* XXX - handle symlinks and '..' in path outside chroot */
67
2
  const int len =
68
2
      snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path);
69
2
  if (len >= ssizeof(pathbuf)) {
70
0
      errno = ENAMETOOLONG;
71
0
      sudo_warn("%s%s", runchroot, path);
72
0
      goto done;
73
0
  }
74
2
  path = pathbuf;
75
2
    }
76
77
13
    TAILQ_FOREACH(digest, digests, entries) {
78
  /* Compute file digest if needed. */
79
13
  if (digest->digest_type != digest_type) {
80
13
      free(file_digest);
81
13
      file_digest = sudo_filedigest(fd, path, digest->digest_type,
82
13
    &digest_len);
83
13
      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
13
      digest_type = digest->digest_type;
89
13
  }
90
13
  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
13
  sudoers_digest = malloc(digest_len); // -V614
97
13
  if (sudoers_digest == NULL) {
98
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
99
0
      goto done;
100
0
  }
101
13
  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
13
  } else {
111
      /* Convert base64 to binary. */
112
13
      size_t len = base64_decode(digest->digest_str, sudoers_digest,
113
13
    digest_len);
114
13
      if (len == (size_t)-1)
115
0
    goto bad_format;
116
13
      if (len != digest_len) {
117
13
    sudo_warnx(
118
13
        U_("digest for %s (%s) bad length %zu, expected %zu"),
119
13
        path, digest->digest_str, len, digest_len);
120
13
    goto done;
121
13
      }
122
13
  }
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
13
done:
140
13
    if (fd2 != -1)
141
2
  close(fd2);
142
13
    free(sudoers_digest);
143
13
    free(file_digest);
144
13
    debug_return_int(matched);
145
13
}