Skip to content

Commit 88bfaa1

Browse files
committed
Add a wait to redirect Krb5 tracing to our stderr
The proccess stderr may be redirected to a socket or a pipe by the parent process (which is what systemd does for example). When that happen we can't simply pass a valid path to stderr so that libkrb5 can send the tracing via the KRB5_TRACE environment variable to a our stderr, passing /dev/stderr in this case will just result in an open error and tracing output going nowhere. To handle this we create our own fifo to pass to libkrb5 and then we create a thread that reads the incoming data and pipes it back to the stderr we've been given by our parent. Signed-off-by: Simo Sorce <[email protected]>
1 parent 72ef228 commit 88bfaa1

File tree

6 files changed

+265
-8
lines changed

6 files changed

+265
-8
lines changed

src/gp_debug.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,53 @@
33
#include "config.h"
44
#include <stdbool.h>
55
#include <stdlib.h>
6+
#include "gp_proxy.h"
67
#include "gp_debug.h"
78
#include "gp_log.h"
89

910
/* global debug switch */
1011
int gp_debug = 0;
1112

12-
void gp_debug_toggle(int level)
13+
14+
void (*gp_debug_setup_k5_trace_fn)(int) = NULL;
15+
16+
void gp_debug_set_krb5_tracing_fn(void (*fn)(int))
1317
{
14-
if (level <= gp_debug)
15-
return;
18+
if (gp_debug_setup_k5_trace_fn != NULL) {
19+
/* deactivate potential previously set tracing
20+
* facility */
21+
gp_debug_setup_k5_trace_fn(0);
22+
}
23+
24+
gp_debug_setup_k5_trace_fn = fn;
1625

17-
if (level >= 3 && !getenv("KRB5_TRACE"))
18-
setenv("KRB5_TRACE", "/dev/stderr", 1);
26+
/* the tracing setup function may be set after the
27+
* initial debug level is set via configuration.
28+
* Make sure to immedaiately call it if debug level is
29+
* already above 3 */
30+
if (gp_debug >= 3 && !getenv("KRB5_TRACE")) {
31+
gp_debug_setup_k5_trace_fn(1);
32+
}
33+
}
34+
35+
void gp_debug_toggle(int level)
36+
{
37+
if (level >= 3 && !getenv("KRB5_TRACE")) {
38+
if (gp_debug_setup_k5_trace_fn) {
39+
gp_debug_setup_k5_trace_fn(1);
40+
} else {
41+
setenv("KRB5_TRACE", "/dev/stderr", 1);
42+
}
43+
} else if (level < 3) {
44+
if (gp_debug_setup_k5_trace_fn) {
45+
gp_debug_setup_k5_trace_fn(0);
46+
} else {
47+
unsetenv("KRB5_TRACE");
48+
}
49+
}
1950

2051
gp_debug = level;
21-
GPDEBUG("Debug Enabled (level: %d)\n", level);
52+
GPDEBUG("Debug Level changed to %d\n", gp_debug);
2253
}
2354

2455
void gp_log_failure(gss_OID mech, uint32_t maj, uint32_t min)

src/gp_debug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ void gp_debug_toggle(int);
1616
void gp_debug_printf(const char *format, ...);
1717
void gp_debug_time_printf(const char *format, ...);
1818
void gp_debug_set_conn_id(int id);
19+
void gp_debug_set_krb5_tracing_fn(void (*fn)(int));
1920

2021
#define GPDEBUG(...) do { \
2122
if (gp_debug) { \

src/gp_init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ void init_done(int wait_fd)
110110

111111
void fini_server(void)
112112
{
113+
gp_krb5_fini_tracing();
113114
closelog();
114115
}
115116

src/gp_mgmt.c

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
/* Copyright (C) 2022 the GSS-PROXY contributors, see COPYING for license */
22

3+
#define _GNU_SOURCE
34
#include "config.h"
4-
#include "gp_proxy.h"
5+
#include <errno.h>
6+
#include <fcntl.h>
7+
#include <pthread.h>
8+
#include <stdio.h>
9+
#include <string.h>
10+
#include <sys/epoll.h>
11+
#include <sys/stat.h>
12+
#include <sys/types.h>
513
#include <time.h>
14+
#include <unistd.h>
15+
#include "gp_proxy.h"
616

717
static void idle_terminate(verto_ctx *vctx, verto_ev *ev)
818
{
@@ -67,3 +77,213 @@ void gp_activity_accounting(struct gssproxy_ctx *gpctx,
6777
gpctx->last_activity = now;
6878
}
6979
}
80+
81+
#define MAX_K5_EVENTS 10
82+
static struct k5tracer {
83+
pthread_t tid;
84+
int fd;
85+
} *k5tracer = NULL;
86+
87+
static void *k5tracer_thread(void *pvt UNUSED)
88+
{
89+
struct epoll_event ev, events[MAX_K5_EVENTS];
90+
int num, epollfd;
91+
92+
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
93+
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
94+
95+
fprintf(stderr, "k5tracer_thread started!\n");
96+
fflush(stderr);
97+
98+
epollfd = epoll_create1(EPOLL_CLOEXEC);
99+
if (epollfd == -1) {
100+
fprintf(stderr, "k5tracer_thread, epoll_create1 failed\n");
101+
fflush(stderr);
102+
pthread_exit(NULL);
103+
}
104+
105+
ev.events = EPOLLIN;
106+
ev.data.fd = k5tracer->fd;
107+
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, k5tracer->fd, &ev) == -1) {
108+
fprintf(stderr, "k5tracer_thread, epoll_ctl failed\n");
109+
fflush(stderr);
110+
pthread_exit(NULL);
111+
}
112+
113+
114+
for (;;) {
115+
num = epoll_wait(epollfd, events, MAX_K5_EVENTS, -1);
116+
if (num == -1) {
117+
fprintf(stderr, "k5tracer_thread, epoll_wait failed (%d)\n",
118+
errno);
119+
fflush(stderr);
120+
pthread_exit(NULL);
121+
}
122+
123+
for (int i = 0; i < num; i++) {
124+
if (events[i].events & EPOLLIN) {
125+
char buf[512];
126+
size_t pos = 0;
127+
ssize_t rn;
128+
size_t wn;
129+
130+
for (;;) {
131+
rn = read(events[i].data.fd, buf, 512);
132+
if (rn == -1) {
133+
if (errno != EAGAIN && errno != EINTR ) {
134+
fprintf(stderr, "k5tracer_thread, "
135+
"fatal error on fd %d %d\n",
136+
events[i].data.fd, errno);
137+
break;
138+
}
139+
rn = 0;
140+
}
141+
if (rn == 0) {
142+
/* done, getting input */
143+
break;
144+
}
145+
/* let's hope all gets written, but if not we just
146+
* missed some debugging output and thatis ok, in
147+
* the very unlikely case it happens */
148+
while (rn > 0) {
149+
wn = fwrite(buf + pos, 1, rn, stderr);
150+
if (wn == 0) break;
151+
rn -= wn;
152+
pos += rn;
153+
}
154+
}
155+
}
156+
}
157+
fflush(stderr);
158+
}
159+
}
160+
161+
void free_k5tracer(void)
162+
{
163+
if (k5tracer == NULL) return;
164+
165+
if (k5tracer->fd > 0) {
166+
close(k5tracer->fd);
167+
}
168+
safefree(k5tracer);
169+
}
170+
171+
char *tracing_file_name = NULL;
172+
173+
/* if action == 1 activate KRB5 tracing bridge.
174+
* if action == 0 deactivate it */
175+
void gp_krb5_tracing_setup(int action)
176+
{
177+
pthread_attr_t attr;
178+
int ret;
179+
180+
if (action != 0 && action != 1) {
181+
GPDEBUGN(3, "%s: Unknown action %d\n", __func__, action);
182+
return;
183+
}
184+
185+
if (action == 0) {
186+
if (k5tracer) {
187+
pthread_cancel(k5tracer->tid);
188+
pthread_join(k5tracer->tid, NULL);
189+
unsetenv("KRB5_TRACE");
190+
}
191+
return;
192+
}
193+
194+
/* activate only once */
195+
if (k5tracer != NULL) return;
196+
197+
k5tracer = calloc(1, sizeof(struct k5tracer));
198+
if (k5tracer == NULL) {
199+
ret = ENOMEM;
200+
goto done;
201+
}
202+
203+
/* this name is predictable, but we always unlink it before
204+
* creating a new one with permission only for the current
205+
* user. A race between unilnk and mkfifo will cause failure
206+
* and we'll never open the file that raced us */
207+
if (tracing_file_name == NULL) {
208+
ret = asprintf(&tracing_file_name,
209+
"/tmp/krb5.tracing.%ld", (long)getpid());
210+
if (ret == -1) {
211+
ret = errno;
212+
goto done;
213+
}
214+
}
215+
216+
ret = unlink(tracing_file_name);
217+
if (ret == -1 && errno != ENOENT) {
218+
ret = errno;
219+
GPDEBUGN(3, "%s: unlink(%s) failed\n", __func__, tracing_file_name);
220+
goto done;
221+
}
222+
223+
ret = mkfifo(tracing_file_name, 0600);
224+
if (ret == -1) {
225+
ret = errno;
226+
GPDEBUGN(3, "%s: mkfifo(%s) failed\n", __func__, tracing_file_name);
227+
goto done;
228+
}
229+
230+
k5tracer->fd = open(tracing_file_name, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
231+
if (k5tracer->fd == -1) {
232+
ret = errno;
233+
GPDEBUGN(3, "%s: open(%s) failed\n", __func__, tracing_file_name);
234+
goto done;
235+
} else if (k5tracer->fd <= 2) {
236+
/* we do not expect stdio to be closed because we need to use it
237+
* to forward the tracing, if the fd return is smaller than stderr
238+
* consider stdio messed up and just ignore tracing */
239+
ret = EINVAL;
240+
GPDEBUGN(3, "%s: open(%s) returned fd too low: %d",
241+
__func__, tracing_file_name, k5tracer->fd);
242+
close(k5tracer->fd);
243+
k5tracer->fd = 0;
244+
goto done;
245+
}
246+
247+
ret = pthread_attr_init(&attr);
248+
if (ret) {
249+
GPDEBUGN(3, "%s: pthread_attr_init failed: %d", __func__, ret);
250+
goto done;
251+
}
252+
253+
ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
254+
if (ret) {
255+
GPDEBUGN(3, "%s: pthread_attr_setdetachstate: %d", __func__, ret);
256+
}
257+
258+
ret = pthread_create(&k5tracer->tid, &attr, k5tracer_thread, NULL);
259+
if (ret) {
260+
pthread_attr_destroy(&attr);
261+
262+
GPDEBUGN(3, "%s: pthread_create failed: %d", __func__, ret);
263+
goto done;
264+
}
265+
266+
pthread_attr_destroy(&attr);
267+
setenv("KRB5_TRACE", tracing_file_name, 1);
268+
269+
ret = 0;
270+
271+
done:
272+
if (ret) {
273+
char errstr[128]; /* reasonable error str length */
274+
GPDEBUGN(3, "%s: Failed to set up krb5 tracing thread: [%s](%d)\n",
275+
__func__, strerror_r(ret, errstr, 128), ret);
276+
free_k5tracer();
277+
}
278+
return;
279+
}
280+
281+
void gp_krb5_fini_tracing(void)
282+
{
283+
if (tracing_file_name) {
284+
/* just in case */
285+
gp_krb5_tracing_setup(0);
286+
/* remove this one if there */
287+
unlink(tracing_file_name);
288+
}
289+
}

src/gp_proxy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ int clear_bound_caps(void);
133133
void idle_handler(struct gssproxy_ctx *gpctx);
134134
void gp_activity_accounting(struct gssproxy_ctx *gpctx,
135135
ssize_t rb, ssize_t wb);
136+
void gp_krb5_tracing_setup(int action);
137+
void gp_krb5_fini_tracing(void);
136138

137139
/* from gp_socket.c */
138140
void free_unix_socket(verto_ctx *ctx, verto_ev *ev);

src/gssproxy.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ int main(int argc, const char *argv[])
8686
goto cleanup;
8787
}
8888

89+
/* set tracing function before handling debug level */
90+
gp_debug_set_krb5_tracing_fn(&gp_krb5_tracing_setup);
91+
8992
if (opt_debug || opt_debug_level > 0) {
9093
if (opt_debug_level == 0) opt_debug_level = 1;
9194
gp_debug_toggle(opt_debug_level);
@@ -197,7 +200,6 @@ int main(int argc, const char *argv[])
197200

198201
fini_server();
199202

200-
201203
ret = 0;
202204

203205
cleanup:

0 commit comments

Comments
 (0)