Coverage Report

Created: 2025-06-03 06:05

/src/sudo/lib/util/fatal.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2004-2005, 2010-2015, 2017-2018
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
20
/*
21
 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
22
 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
23
 */
24
25
#include <config.h>
26
27
#include <errno.h>
28
#include <netdb.h>
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#ifdef HAVE_STDBOOL_H
33
# include <stdbool.h>
34
#else
35
# include <compat/stdbool.h>
36
#endif /* HAVE_STDBOOL_H */
37
#include <unistd.h>
38
#ifndef HAVE_GETADDRINFO
39
# include <compat/getaddrinfo.h>
40
#endif
41
42
#include <sudo_compat.h>
43
#include <sudo_fatal.h>
44
#include <sudo_gettext.h>
45
#include <sudo_queue.h>
46
#include <sudo_util.h>
47
#include <sudo_plugin.h>
48
49
struct sudo_fatal_callback {
50
    SLIST_ENTRY(sudo_fatal_callback) entries;
51
    void (*func)(void);
52
};
53
SLIST_HEAD(sudo_fatal_callback_list, sudo_fatal_callback);
54
55
static struct sudo_fatal_callback_list callbacks = SLIST_HEAD_INITIALIZER(&callbacks);
56
static sudo_conv_t sudo_warn_conversation;
57
static sudo_warn_setlocale_t sudo_warn_setlocale;
58
static sudo_warn_setlocale_t sudo_warn_setlocale_prev;
59
60
static void warning(const char * restrict errstr, const char * restrict fmt, va_list ap);
61
62
static void
63
do_cleanup(void)
64
0
{
65
0
    struct sudo_fatal_callback *cb;
66
67
    /* Run callbacks, removing them from the list as we go. */
68
0
    while ((cb = SLIST_FIRST(&callbacks)) != NULL) {
69
0
  SLIST_REMOVE_HEAD(&callbacks, entries);
70
0
  cb->func();
71
0
  free(cb);
72
0
    }
73
0
}
74
75
sudo_noreturn void
76
sudo_fatal_nodebug_v1(const char * restrict fmt, ...)
77
0
{
78
0
    va_list ap;
79
80
0
    va_start(ap, fmt);
81
0
    warning(strerror(errno), fmt, ap);
82
0
    va_end(ap);
83
0
    do_cleanup();
84
0
    exit(EXIT_FAILURE);
85
0
}
86
87
sudo_noreturn void
88
sudo_fatalx_nodebug_v1(const char * restrict fmt, ...)
89
0
{
90
0
    va_list ap;
91
92
0
    va_start(ap, fmt);
93
0
    warning(NULL, fmt, ap);
94
0
    va_end(ap);
95
0
    do_cleanup();
96
0
    exit(EXIT_FAILURE);
97
0
}
98
99
sudo_noreturn void
100
sudo_vfatal_nodebug_v1(const char * restrict fmt, va_list ap)
101
0
{
102
0
    warning(strerror(errno), fmt, ap);
103
0
    do_cleanup();
104
0
    exit(EXIT_FAILURE);
105
0
}
106
107
sudo_noreturn void
108
sudo_vfatalx_nodebug_v1(const char * restrict fmt, va_list ap)
109
0
{
110
0
    warning(NULL, fmt, ap);
111
0
    do_cleanup();
112
0
    exit(EXIT_FAILURE);
113
0
}
114
115
void
116
sudo_warn_nodebug_v1(const char * restrict fmt, ...)
117
312
{
118
312
    va_list ap;
119
120
312
    va_start(ap, fmt);
121
312
    warning(strerror(errno), fmt, ap);
122
312
    va_end(ap);
123
312
}
124
125
void
126
sudo_warnx_nodebug_v1(const char * restrict fmt, ...)
127
0
{
128
0
    va_list ap;
129
0
    va_start(ap, fmt);
130
0
    warning(NULL, fmt, ap);
131
0
    va_end(ap);
132
0
}
133
134
void
135
sudo_vwarn_nodebug_v1(const char * restrict fmt, va_list ap)
136
0
{
137
0
    warning(strerror(errno), fmt, ap);
138
0
}
139
140
void
141
sudo_vwarnx_nodebug_v1(const char * restrict fmt, va_list ap)
142
0
{
143
0
    warning(NULL, fmt, ap);
144
0
}
145
146
sudo_noreturn void
147
sudo_gai_fatal_nodebug_v1(int errnum, const char * restrict fmt, ...)
148
0
{
149
0
    va_list ap;
150
151
0
    va_start(ap, fmt);
152
0
    warning(gai_strerror(errnum), fmt, ap);
153
0
    va_end(ap);
154
0
    do_cleanup();
155
0
    exit(EXIT_FAILURE);
156
0
}
157
158
sudo_noreturn void
159
sudo_gai_vfatal_nodebug_v1(int errnum, const char * restrict fmt, va_list ap)
160
0
{
161
0
    warning(gai_strerror(errnum), fmt, ap);
162
0
    do_cleanup();
163
0
    exit(EXIT_FAILURE);
164
0
}
165
166
void
167
sudo_gai_warn_nodebug_v1(int errnum, const char * restrict fmt, ...)
168
0
{
169
0
    va_list ap;
170
171
0
    va_start(ap, fmt);
172
0
    warning(gai_strerror(errnum), fmt, ap);
173
0
    va_end(ap);
174
0
}
175
176
void
177
sudo_gai_vwarn_nodebug_v1(int errnum, const char * restrict fmt, va_list ap)
178
0
{
179
0
    warning(gai_strerror(errnum), fmt, ap);
180
0
}
181
182
static void
183
warning(const char * restrict errstr, const char * restrict fmt, va_list ap)
184
312
{
185
312
    int cookie;
186
312
    const int saved_errno = errno;
187
188
    /* Set user locale if setter was specified. */
189
312
    if (sudo_warn_setlocale != NULL)
190
0
  sudo_warn_setlocale(false, &cookie);
191
192
312
    if (sudo_warn_conversation != NULL) {
193
312
  struct sudo_conv_message msgs[6];
194
312
  char static_buf[1024], *buf = static_buf;
195
312
  int nmsgs = 0;
196
197
  /* Use conversation function. */
198
312
        msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
199
312
  msgs[nmsgs++].msg = getprogname();
200
312
        if (fmt != NULL) {
201
312
    va_list ap2;
202
312
    int buflen;
203
204
    /* Use static buffer if possible, else dynamic. */
205
312
    va_copy(ap2, ap);
206
312
    buflen = vsnprintf(static_buf, sizeof(static_buf), fmt, ap2);
207
312
    va_end(ap2);
208
312
    if (buflen >= ssizeof(static_buf)) {
209
        /* Not enough room in static buf, allocate dynamically. */
210
29
        if (vasprintf(&buf, fmt, ap) == -1)
211
0
      buf = static_buf;
212
29
    }
213
312
    if (buflen > 0) {
214
312
        msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
215
312
        msgs[nmsgs++].msg = ": ";
216
312
        msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
217
312
        msgs[nmsgs++].msg = buf;
218
312
    }
219
312
        }
220
312
        if (errstr != NULL) {
221
312
      msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
222
312
      msgs[nmsgs++].msg = ": ";
223
312
      msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
224
312
      msgs[nmsgs++].msg = errstr;
225
312
        }
226
312
  msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
227
312
  msgs[nmsgs++].msg = "\n";
228
312
  sudo_warn_conversation(nmsgs, msgs, NULL, NULL);
229
312
  if (buf != static_buf)
230
29
      free(buf);
231
312
    } else {
232
  /* Write to the standard error. */
233
0
        fputs(getprogname(), stderr);
234
0
        if (fmt != NULL) {
235
0
                fputs(": ", stderr);
236
0
                vfprintf(stderr, fmt, ap);
237
0
        }
238
0
        if (errstr != NULL) {
239
0
            fputs(": ", stderr);
240
0
            fputs(errstr, stderr);
241
0
        }
242
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
243
        if (sudo_term_is_raw(fileno(stderr)))
244
            putc('\r', stderr);
245
#endif
246
0
        putc('\n', stderr);
247
0
    }
248
249
    /* Restore old locale as needed. */
250
312
    if (sudo_warn_setlocale != NULL)
251
0
  sudo_warn_setlocale(true, &cookie);
252
253
    /* Do not clobber errno. */
254
312
    errno = saved_errno;
255
312
}
256
257
/*
258
 * Register a callback to be run when sudo_fatal()/sudo_fatalx() is called.
259
 */
260
int
261
sudo_fatal_callback_register_v1(sudo_fatal_callback_t func)
262
0
{
263
0
    struct sudo_fatal_callback *cb;
264
265
    /* Do not register the same callback twice.  */
266
0
    SLIST_FOREACH(cb, &callbacks, entries) {
267
0
  if (func == cb->func)
268
0
      return -1;   /* dupe! */
269
0
    }
270
271
    /* Allocate and insert new callback. */
272
0
    cb = malloc(sizeof(*cb));
273
0
    if (cb == NULL)
274
0
  return -1;
275
0
    cb->func = func;
276
0
    SLIST_INSERT_HEAD(&callbacks, cb, entries);
277
278
0
    return 0;
279
0
}
280
281
/*
282
 * Deregister a sudo_fatal()/sudo_fatalx() callback.
283
 */
284
int
285
sudo_fatal_callback_deregister_v1(sudo_fatal_callback_t func)
286
0
{
287
0
    struct sudo_fatal_callback *cb, *prev = NULL;
288
289
    /* Search for callback and remove if found, dupes are not allowed. */
290
0
    SLIST_FOREACH(cb, &callbacks, entries) {
291
0
  if (cb->func == func) {
292
0
      if (prev == NULL)
293
0
    SLIST_REMOVE_HEAD(&callbacks, entries);
294
0
      else
295
0
    SLIST_REMOVE_AFTER(prev, entries);
296
0
      free(cb);
297
0
      return 0;
298
0
  }
299
0
  prev = cb;
300
0
    }
301
302
0
    return -1;
303
0
}
304
305
/*
306
 * Set the conversation function to use for output instead of the
307
 * standard error.  If conv is NULL, switch back to standard error.
308
 */
309
void
310
sudo_warn_set_conversation_v1(sudo_conv_t conv)
311
394
{
312
394
    sudo_warn_conversation = conv;
313
394
}
314
315
/*
316
 * Set the locale function so the plugin can use a non-default
317
 * locale for user warnings.
318
 */
319
void
320
sudo_warn_set_locale_func_v1(sudo_warn_setlocale_t func)
321
0
{
322
0
    sudo_warn_setlocale_prev = sudo_warn_setlocale;
323
0
    sudo_warn_setlocale = func;
324
0
}
325
326
#ifdef HAVE_LIBINTL_H
327
char *
328
sudo_warn_gettext_v1(const char *domainname, const char *msgid)
329
312
{
330
312
    int cookie;
331
312
    char *msg;
332
333
    /* Set user locale if setter was specified. */
334
312
    if (sudo_warn_setlocale != NULL)
335
0
  sudo_warn_setlocale(false, &cookie);
336
337
312
    msg = dgettext(domainname, msgid);
338
339
    /* Restore old locale as needed. */
340
312
    if (sudo_warn_setlocale != NULL)
341
0
  sudo_warn_setlocale(true, &cookie);
342
343
312
    return msg;
344
312
}
345
#endif /* HAVE_LIBINTL_H */