Bug Summary

File:examples/openid20/smtp-server-openid20.c
Warning:line 229, column 2
Value stored to 'rc' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name smtp-server-openid20.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/gsasl/gsasl/examples/openid20 -fcoverage-compilation-dir=/builds/gsasl/gsasl/examples/openid20 -resource-dir /usr/lib64/llvm20/bin/../../../lib/clang/20 -D HAVE_CONFIG_H -I . -I ../.. -I ../../gl -I ../../gl -I ../../lib/src -I ../../lib/src -internal-isystem /usr/lib64/llvm20/bin/../../../lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/15/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wwrite-strings -Wno-analyzer-malloc-leak -Wno-sign-compare -Wno-system-headers -fconst-strings -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/gsasl/gsasl/clang-analyzer/2025-05-22-230929-18661-1 -x c smtp-server-openid20.c
1/* smtp-server-openid20.c --- Example SMTP server with OpenID 2.0 support
2 * Copyright (C) 2012-2025 Simon Josefsson
3 *
4 * This file is part of GNU SASL.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <config.h>
22
23/* This is based on ../smtp-server.c but adds support for OpenID 2.0.
24 See README for instructions. */
25
26/* This is a minimal SMTP server with GNU SASL authentication support.
27 The only valid password is "sesam". This server will complete
28 authentications using LOGIN, PLAIN, DIGEST-MD5, CRAM-MD5, and
29 SCRAM-SHA-1. It accepts an optional command line parameter
30 specifying the service name (i.e., a numerical port number or
31 /etc/services name). By default it listens on port "2000". */
32
33#include <stdio.h>
34#include <unistd.h>
35#include <time.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38
39#include <string.h>
40#include <stdlib.h>
41#include <stdarg.h>
42#include <netdb.h>
43#include <signal.h>
44
45#include <gsasl.h>
46
47static char *store_path;
48static char *realm;
49static char *return_to;
50
51static void
52hex_encode (char *dst, const char *src, size_t len)
53{
54 static const char trans[] = "0123456789abcdef";
55
56 while (len--)
57 {
58 *dst++ = trans[(*src >> 4) & 0xf];
59 *dst++ = trans[*src++ & 0xf];
60 }
61
62 *dst = '\0';
63}
64
65static int
66write_file (const char *filename, const char *data)
67{
68 FILE *fh;
69 int rc = 0;
70
71 fh = fopen (filename, "w");
72 if (!fh)
73 {
74 perror ("fopen");
75 return -1;
76 }
77
78 if (fputs (data, fh) <= 0)
79 rc = -1;
80
81 if (fclose (fh) != 0)
82 return -1;
83
84 return rc;
85}
86
87static char *
88get_redirect_url (Gsasl_session *sctx)
89{
90 FILE *fh;
91 char *tmp, *tmp2;
92 char *line = NULL((void*)0);
93 size_t n = 0;
94 const char *nonce = gsasl_session_hook_get (sctx);
95 int rc;
96
97 rc = asprintf (&tmp, "%s", store_path);
98 if (rc <= 0)
99 {
100 perror ("asprintf");
101 return NULL((void*)0);
102 }
103 mkdir (tmp, 0770);
104 free (tmp);
105
106 rc = asprintf (&tmp, "%s/state", store_path);
107 if (rc <= 0)
108 {
109 perror ("asprintf");
110 return NULL((void*)0);
111 }
112 mkdir (tmp, 0770);
113 free (tmp);
114
115 rc = asprintf (&tmp, "%s/state/%s", store_path, nonce);
116 if (rc <= 0)
117 {
118 perror ("asprintf");
119 return NULL((void*)0);
120 }
121 mkdir (tmp, 0770);
122 free (tmp);
123
124 rc = asprintf (&tmp, "%s/state/%s/openid_url", store_path, nonce);
125 if (rc <= 0)
126 {
127 perror ("asprintf");
128 return NULL((void*)0);
129 }
130 if (write_file (tmp, gsasl_property_fast (sctx, GSASL_AUTHID)))
131 return NULL((void*)0);
132 free (tmp);
133
134 rc = asprintf (&tmp, "%s/state/%s/realm", store_path, nonce);
135 if (rc <= 0)
136 {
137 perror ("asprintf");
138 return NULL((void*)0);
139 }
140 if (write_file (tmp, realm))
141 return NULL((void*)0);
142 free (tmp);
143
144 rc = asprintf (&tmp, "%s/state/%s/return_to", store_path, nonce);
145 if (rc <= 0)
146 {
147 perror ("asprintf");
148 return NULL((void*)0);
149 }
150 rc = asprintf (&tmp2, "%s/%s", return_to, nonce);
151 if (rc <= 0)
152 {
153 perror ("asprintf");
154 return NULL((void*)0);
155 }
156 if (write_file (tmp, tmp2))
157 return NULL((void*)0);
158 free (tmp);
159 free (tmp2);
160
161 rc =
162 asprintf (&tmp, "gsasl-openid20-redirect.php %s %s", store_path, nonce);
163 if (rc <= 0)
164 {
165 perror ("asprintf");
166 return NULL((void*)0);
167 }
168 fh = popen (tmp, "r");
169 free (tmp);
170 if (!fh)
171 {
172 perror ("popen");
173 return NULL((void*)0);
174 }
175 while (getline (&line, &n, fh) >= 0)
176 printf ("gsasl-openid20-redirect.php: %s", line);
177 pclose (fh);
178
179 rc = asprintf (&tmp, "%s/state/%s/redirect_url", store_path, nonce);
180 if (rc <= 0)
181 {
182 perror ("asprintf");
183 return NULL((void*)0);
184 }
185 fh = fopen (tmp, "r");
186 if (!fh)
187 {
188 perror ("fopen");
189 return NULL((void*)0);
190 }
191 if (getline (&line, &n, fh) <= 0)
192 {
193 perror ("getline");
194 return NULL((void*)0);
195 }
196 fclose (fh);
197
198 return line;
199}
200
201static int
202callback (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
203{
204 int rc = GSASL_NO_CALLBACK;
205 FILE *fh;
206 char *line = NULL((void*)0);
207 size_t n = 0;
208 const char *nonce = gsasl_session_hook_get (sctx);
209 char *tmp;
210
211 (void) ctx;
212
213 switch (prop)
214 {
215 case GSASL_OPENID20_REDIRECT_URL:
216 {
217 line = get_redirect_url (sctx);
218 if (line == NULL((void*)0))
219 rc = GSASL_AUTHENTICATION_ERROR;
220 else
221 rc = gsasl_property_set (sctx, prop, line);
222 }
223 break;
224
225 case GSASL_VALIDATE_OPENID20:
226 {
227 time_t start = time (NULL((void*)0));
228
229 rc = GSASL_AUTHENTICATION_ERROR;
Value stored to 'rc' is never read
230 do
231 {
232 sleep (1);
233
234 rc = asprintf (&tmp, "%s/state/%s/success", store_path, nonce);
235 if (rc <= 0)
236 {
237 perror ("asprintf");
238 break;
239 }
240 fh = fopen (tmp, "r");
241 free (tmp);
242 if (!fh)
243 {
244 rc = asprintf (&tmp, "%s/state/%s/fail", store_path, nonce);
245 if (rc <= 0)
246 {
247 perror ("asprintf");
248 break;
249 }
250 fh = fopen (tmp, "r");
251 free (tmp);
252 if (!fh)
253 {
254 puts ("waiting");
255 continue;
256 }
257
258 if (getline (&line, &n, fh) > 0)
259 printf ("fail: %s\n", line);
260 fclose (fh);
261
262 rc = GSASL_AUTHENTICATION_ERROR;
263 break;
264 }
265
266 if (getline (&line, &n, fh) > 0)
267 printf ("claimed id: %s\n", line);
268 fclose (fh);
269
270 rc = gsasl_property_set (sctx, GSASL_AUTHID, line);
271 if (rc != GSASL_OK)
272 {
273 printf ("Failure in gsasl_property_set (%d): %s\n",
274 rc, gsasl_strerror (rc));
275 break;
276 }
277
278 rc = asprintf (&tmp, "%s/state/%s/sreg", store_path, nonce);
279 if (rc <= 0)
280 {
281 perror ("asprintf");
282 break;
283 }
284 fh = fopen (tmp, "r");
285 free (tmp);
286 if (fh)
287 {
288 if (getline (&line, &n, fh) > 0)
289 {
290 printf ("sreg: %s\n", line);
291 rc = gsasl_property_set (sctx,
292 GSASL_OPENID20_OUTCOME_DATA,
293 line);
294 if (rc != GSASL_OK)
295 {
296 printf ("Failure in gsasl_property_set (%d): %s\n",
297 rc, gsasl_strerror (rc));
298 break;
299 }
300 }
301 fclose (fh);
302 }
303
304 rc = GSASL_OK;
305 break;
306 }
307 while (time (NULL((void*)0)) - start < 30);
308 }
309 break;
310
311 case GSASL_PASSWORD:
312 rc = gsasl_property_set (sctx, prop, "sesam");
313 break;
314
315 default:
316 /* You may want to log (at debug verbosity level) that an
317 unknown property was requested here, possibly after filtering
318 known rejected property requests. */
319 break;
320 }
321
322 return rc;
323}
324
325static ssize_t
326gettrimline (char **line, size_t *n, FILE *fh)
327{
328 ssize_t s = getline (line, n, fh);
329
330 if (s >= 2)
331 {
332 if ((*line)[strlen (*line) - 1] == '\n')
333 (*line)[strlen (*line) - 1] = '\0';
334 if ((*line)[strlen (*line) - 1] == '\r')
335 (*line)[strlen (*line) - 1] = '\0';
336
337 printf ("C: %s\n", *line);
338 }
339
340 return s;
341}
342
343#define print(fh, ...)printf ("S: "), printf (...), fprintf (fh, ...) \
344 printf ("S: "), printf (__VA_ARGS__), fprintf (fh, __VA_ARGS__)
345
346static void
347server_auth (FILE *fh, Gsasl_session *session)
348{
349 char *line = NULL((void*)0);
350 size_t n = 0;
351 char *p;
352 int rc;
353 /* The nonce value MUST be at least 2^32 large and large enough to
354 handle well in excess of the number of concurrent transactions a
355 SASL server shall see. */
356 char bin_nonce[32];
357 char nonce[2 * sizeof (bin_nonce) + 1];
358
359 gsasl_nonce (bin_nonce, sizeof (bin_nonce));
360 hex_encode (nonce, bin_nonce, sizeof (bin_nonce));
361 gsasl_session_hook_set (session, nonce);
362
363 /* The ordering and the type of checks in the following loop has to
364 be adapted for each protocol depending on its SASL properties.
365 SMTP is a "server-first" SASL protocol. This implementation do
366 not support piggy-backing of the initial client challenge nor
367 piggy-backing of the terminating server response. See RFC 2554
368 and RFC 4422 for terminology. That profile results in the
369 following loop structure. Ask on the help-gsasl list if you are
370 uncertain. */
371 do
372 {
373 rc = gsasl_step64 (session, line, &p);
374 if (rc == GSASL_NEEDS_MORE || (rc == GSASL_OK && p && *p))
375 {
376 print (fh, "334 %s\n", p)printf ("S: "), printf ("334 %s\n", p), fprintf (fh, "334 %s\n"
, p)
;
377 gsasl_free (p);
378
379 if (gettrimline (&line, &n, fh) < 0)
380 {
381 print (fh, "221 localhost getline failure\n")printf ("S: "), printf ("221 localhost getline failure\n"), fprintf
(fh, "221 localhost getline failure\n")
;
382 goto done;
383 }
384 }
385 }
386 while (rc == GSASL_NEEDS_MORE);
387
388 if (rc != GSASL_OK)
389 {
390 print (fh, "535 gsasl_step64 (%d): %s\n", rc, gsasl_strerror (rc))printf ("S: "), printf ("535 gsasl_step64 (%d): %s\n", rc, gsasl_strerror
(rc)), fprintf (fh, "535 gsasl_step64 (%d): %s\n", rc, gsasl_strerror
(rc))
;
391 goto done;
392 }
393
394 {
395 const char *authid = gsasl_property_fast (session, GSASL_AUTHID);
396 const char *authzid = gsasl_property_fast (session, GSASL_AUTHZID);
397 print (fh, "235 OK [authid: %s authzid: %s]\n",printf ("S: "), printf ("235 OK [authid: %s authzid: %s]\n", authid
? authid : "N/A", authzid ? authzid : "N/A"), fprintf (fh, "235 OK [authid: %s authzid: %s]\n"
, authid ? authid : "N/A", authzid ? authzid : "N/A")
398 authid ? authid : "N/A", authzid ? authzid : "N/A")printf ("S: "), printf ("235 OK [authid: %s authzid: %s]\n", authid
? authid : "N/A", authzid ? authzid : "N/A"), fprintf (fh, "235 OK [authid: %s authzid: %s]\n"
, authid ? authid : "N/A", authzid ? authzid : "N/A")
;
399 }
400
401done:
402 free (line);
403}
404
405static void
406smtp (FILE *fh, Gsasl *ctx)
407{
408 char *line = NULL((void*)0);
409 size_t n = 0;
410 int rc;
411
412 print (fh, "220 localhost ESMTP GNU SASL smtp-server\n")printf ("S: "), printf ("220 localhost ESMTP GNU SASL smtp-server\n"
), fprintf (fh, "220 localhost ESMTP GNU SASL smtp-server\n")
;
413
414 while (gettrimline (&line, &n, fh) >= 0)
415 {
416 if (strncmp (line, "EHLO ", 5) == 0 || strncmp (line, "ehlo ", 5) == 0)
417 {
418 char *mechlist;
419
420 rc = gsasl_server_mechlist (ctx, &mechlist);
421 if (rc != GSASL_OK)
422 {
423 print (fh, "221 localhost gsasl_server_mechlist (%d): %s\n",printf ("S: "), printf ("221 localhost gsasl_server_mechlist (%d): %s\n"
, rc, gsasl_strerror (rc)), fprintf (fh, "221 localhost gsasl_server_mechlist (%d): %s\n"
, rc, gsasl_strerror (rc))
424 rc, gsasl_strerror (rc))printf ("S: "), printf ("221 localhost gsasl_server_mechlist (%d): %s\n"
, rc, gsasl_strerror (rc)), fprintf (fh, "221 localhost gsasl_server_mechlist (%d): %s\n"
, rc, gsasl_strerror (rc))
;
425 goto done;
426 }
427
428 print (fh, "250-localhost\n")printf ("S: "), printf ("250-localhost\n"), fprintf (fh, "250-localhost\n"
)
;
429 print (fh, "250 AUTH %s\n", mechlist)printf ("S: "), printf ("250 AUTH %s\n", mechlist), fprintf (
fh, "250 AUTH %s\n", mechlist)
;
430
431 gsasl_free (mechlist);
432 }
433 else if (strncmp (line, "AUTH ", 5) == 0
434 || strncmp (line, "auth ", 5) == 0)
435 {
436 Gsasl_session *session = NULL((void*)0);
437
438 if ((rc = gsasl_server_start (ctx, line + 5, &session)) != GSASL_OK)
439 {
440 print (fh, "221 localhost gsasl_server_start (%d): %s\n",printf ("S: "), printf ("221 localhost gsasl_server_start (%d): %s\n"
, rc, gsasl_strerror (rc)), fprintf (fh, "221 localhost gsasl_server_start (%d): %s\n"
, rc, gsasl_strerror (rc))
441 rc, gsasl_strerror (rc))printf ("S: "), printf ("221 localhost gsasl_server_start (%d): %s\n"
, rc, gsasl_strerror (rc)), fprintf (fh, "221 localhost gsasl_server_start (%d): %s\n"
, rc, gsasl_strerror (rc))
;
442 goto done;
443 }
444
445 server_auth (fh, session);
446
447 gsasl_finish (session);
448 }
449 else if (strncmp (line, "QUIT", 4) == 0
450 || strncmp (line, "quit", 4) == 0)
451 {
452 print (fh, "221 localhost QUIT\n")printf ("S: "), printf ("221 localhost QUIT\n"), fprintf (fh,
"221 localhost QUIT\n")
;
453 goto done;
454 }
455 else
456 print (fh, "500 unrecognized command\n")printf ("S: "), printf ("500 unrecognized command\n"), fprintf
(fh, "500 unrecognized command\n")
;
457 }
458
459 print (fh, "221 localhost getline failure\n")printf ("S: "), printf ("221 localhost getline failure\n"), fprintf
(fh, "221 localhost getline failure\n")
;
460
461done:
462 free (line);
463}
464
465int
466main (int argc, char *argv[])
467{
468 const char *service = argc > 1 ? argv[1] : "2000";
469 volatile int run = 1;
470 struct addrinfo hints, *addrs;
471 int sockfd;
472 int rc;
473 int yes = 1;
474 Gsasl *ctx;
475
476 setvbuf (stdoutstdout, NULL((void*)0), _IONBF2, 0);
477
478 if (argc != 5)
479 {
480 printf ("Usage: %s PORT STORE-PATH REALM RETURN-TO\n", argv[0]);
481 exit (EXIT_FAILURE1);
482 }
483 store_path = argv[2];
484 realm = argv[3];
485 return_to = argv[4];
486
487 rc = gsasl_init (&ctx);
488 if (rc < 0)
489 {
490 printf ("gsasl_init (%d): %s\n", rc, gsasl_strerror (rc));
491 exit (EXIT_FAILURE1);
492 }
493
494 printf ("%s [gsasl header %s library %s]\n",
495 argv[0], GSASL_VERSION"2.2.2.3-6d55", gsasl_check_version (NULL((void*)0)));
496
497 gsasl_callback_set (ctx, callback);
498
499 memset (&hints, 0, sizeof (hints));
500 hints.ai_flags = AI_PASSIVE0x0001 | AI_ADDRCONFIG0x0020;
501 hints.ai_socktype = SOCK_STREAMSOCK_STREAM;
502
503 rc = getaddrinfo (NULL((void*)0), service, &hints, &addrs);
504 if (rc < 0)
505 {
506 printf ("getaddrinfo: %s\n", gai_strerror (rc));
507 exit (EXIT_FAILURE1);
508 }
509
510 sockfd = socket (addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
511 if (sockfd < 0)
512 {
513 perror ("socket");
514 exit (EXIT_FAILURE1);
515 }
516
517 if (setsockopt (sockfd, SOL_SOCKET1, SO_REUSEADDR2, &yes, sizeof (yes)) < 0)
518 {
519 perror ("setsockopt");
520 exit (EXIT_FAILURE1);
521 }
522
523 rc = bind (sockfd, addrs->ai_addr, addrs->ai_addrlen);
524 if (rc < 0)
525 {
526 perror ("bind");
527 exit (EXIT_FAILURE1);
528 }
529
530 freeaddrinfo (addrs);
531
532 rc = listen (sockfd, SOMAXCONN4096);
533 if (rc < 0)
534 {
535 perror ("listen");
536 exit (EXIT_FAILURE1);
537 }
538
539 signal (SIGPIPE13, SIG_IGN((__sighandler_t) 1));
540
541 while (run)
542 {
543 struct sockaddr from;
544 socklen_t fromlen = sizeof (from);
545 char host[NI_MAXHOST1025];
546 int fd;
547 FILE *fh;
548
549 fd = accept (sockfd, &from, &fromlen);
550 if (fd < 0)
551 {
552 perror ("accept");
553 continue;
554 }
555
556 rc = getnameinfo (&from, fromlen, host, sizeof (host),
557 NULL((void*)0), 0, NI_NUMERICHOST1);
558 if (rc == 0)
559 printf ("connection from %s\n", host);
560 else
561 printf ("getnameinfo: %s\n", gai_strerror (rc));
562
563 fh = fdopen (fd, "w+");
564 if (!fh)
565 {
566 perror ("fdopen");
567 close (fd);
568 continue;
569 }
570
571 smtp (fh, ctx);
572
573 fclose (fh);
574 }
575
576 close (sockfd);
577 gsasl_done (ctx);
578
579 return 0;
580}