[server] Added GPG_ALLOW_NO_PW variable and associated test suite support
[fwknop.git] / server / incoming_spa.c
1 /*
2  *****************************************************************************
3  *
4  * File:    incoming_spa.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: Process an incoming SPA data packet for fwknopd.
9  *
10  * Copyright 2010 Damien Stuart (dstuart@dstuart.org)
11  *
12  *  License (GNU Public License):
13  *
14  *  This program is free software; you can redistribute it and/or
15  *  modify it under the terms of the GNU General Public License
16  *  as published by the Free Software Foundation; either version 2
17  *  of the License, or (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
27  *  USA
28  *
29  *****************************************************************************
30 */
31 #include "fwknopd_common.h"
32 #include "netinet_common.h"
33
34 #if HAVE_SYS_WAIT_H
35   #include <sys/wait.h>
36 #endif
37
38 #include "incoming_spa.h"
39 #include "access.h"
40 #include "extcmd.h"
41 #include "log_msg.h"
42 #include "utils.h"
43 #include "fw_util.h"
44 #include "fwknopd_errors.h"
45 #include "replay_cache.h"
46
47 /* Validate and in some cases preprocess/reformat the SPA data.  Return an
48  * error code value if there is any indication the data is not valid spa data.
49 */
50 static int
51 preprocess_spa_data(fko_srv_options_t *opts, const char *src_ip)
52 {
53     spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
54
55     char    *ndx = (char *)&(spa_pkt->packet_data);
56     int      pkt_data_len = spa_pkt->packet_data_len;
57     int      i;
58
59     /* At this point, we can reset the packet data length to 0.  This is our
60      * indicator to the rest of the program that we do not have a current
61      * spa packet to process (after this one that is).
62     */
63     spa_pkt->packet_data_len = 0;
64
65     /* Ignore any SPA packets that contain the Rijndael or GnuPG prefixes
66      * since an attacker might have tacked them on to a previously seen
67      * SPA packet in an attempt to get past the replay check.  And, we're
68      * no worse off since a legitimate SPA packet that happens to include
69      * a prefix after the outer one is stripped off won't decrypt properly
70      * anyway because libfko would not add a new one.
71     */
72     if(strncmp(ndx, B64_RIJNDAEL_SALT, B64_RIJNDAEL_SALT_STR_LEN) == 0)
73         return(SPA_MSG_BAD_DATA);
74
75     if(pkt_data_len > MIN_GNUPG_MSG_SIZE
76             && strncmp(ndx, B64_GPG_PREFIX, B64_GPG_PREFIX_STR_LEN) == 0)
77         return(SPA_MSG_BAD_DATA);
78
79     /* Detect and parse out SPA data from an HTTP request. If the SPA data
80      * starts with "GET /" and the user agent starts with "Fwknop", then
81      * assume it is a SPA over HTTP request.
82     */
83     if(strncasecmp(opts->config[CONF_ENABLE_SPA_OVER_HTTP], "N", 1) == 0
84       && strncasecmp(ndx, "GET /", 5) == 0
85       && strstr(ndx, "User-Agent: Fwknop") != NULL)
86     {
87         /* This looks like an HTTP request, so let's see if we are
88          * configured to accept such request and if so, find the SPA
89          * data.
90         */
91
92         /* Now extract, adjust (convert characters translated by the fwknop
93          * client), and reset the SPA message itself.
94         */
95         strlcpy((char *)spa_pkt->packet_data, ndx+5, pkt_data_len);
96
97         for(i=0; i<pkt_data_len; i++)
98         {
99             if(isspace(*ndx)) /* The first space marks the end of the req */
100             {
101                 *ndx = '\0';
102                 break;
103             }
104             else if(*ndx == '-') /* Convert '-' to '+' */
105                 *ndx = '+';
106             else if(*ndx == '_') /* Convert '_' to '/' */
107                 *ndx = '/';
108
109             ndx++;
110         }
111     }
112
113     /* Require base64-encoded data
114     */
115     if(! is_base64(spa_pkt->packet_data, pkt_data_len))
116         return(SPA_MSG_NOT_SPA_DATA);
117
118
119     /* --DSS:  Are there other checks we can do here ??? */
120
121     /* If we made it here, we have no reason to assume this is not SPA data
122      * (at least until we come up with more checks).
123     */
124     return(FKO_SUCCESS);
125 }
126
127 /* For replay attack detection
128 */
129 static int
130 get_raw_digest(char **digest, char *pkt_data)
131 {
132     fko_ctx_t    ctx = NULL;
133     char        *tmp_digest = NULL;
134     int          res = FKO_SUCCESS;
135
136     /* initialize an FKO context with no decryption key just so
137      * we can get the outer message digest
138     */
139     res = fko_new_with_data(&ctx, (char *)pkt_data, NULL);
140     if(res != FKO_SUCCESS)
141     {
142         log_msg(LOG_WARNING, "Error initializing FKO context from SPA data: %s",
143             fko_errstr(res));
144         fko_destroy(ctx);
145         return(SPA_MSG_FKO_CTX_ERROR);
146     }
147
148     res = fko_set_raw_spa_digest_type(ctx, FKO_DEFAULT_DIGEST);
149     if(res != FKO_SUCCESS)
150     {
151         log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s",
152             fko_errstr(res));
153         fko_destroy(ctx);
154         return(SPA_MSG_DIGEST_ERROR);
155     }
156
157     res = fko_set_raw_spa_digest(ctx);
158     if(res != FKO_SUCCESS)
159     {
160         log_msg(LOG_WARNING, "Error setting digest for SPA data: %s",
161             fko_errstr(res));
162         fko_destroy(ctx);
163         return(SPA_MSG_DIGEST_ERROR);
164     }
165
166     res = fko_get_raw_spa_digest(ctx, &tmp_digest);
167     if(res != FKO_SUCCESS)
168     {
169         log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
170             fko_errstr(res));
171         fko_destroy(ctx);
172         return(SPA_MSG_DIGEST_ERROR);
173     }
174
175     *digest = strdup(tmp_digest);
176
177     if (digest == NULL)
178         return SPA_MSG_ERROR;
179
180     fko_destroy(ctx);
181
182     return res;
183 }
184
185
186 /* Popluate a spa_data struct from an initialized (and populated) FKO context.
187 */
188 static int
189 get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat)
190 {
191     int res = FKO_SUCCESS;
192
193     res = fko_get_username(ctx, &(spdat->username));
194     if(res != FKO_SUCCESS)
195         return(res);
196
197     res = fko_get_timestamp(ctx, &(spdat->timestamp));
198     if(res != FKO_SUCCESS)
199         return(res);
200
201     res = fko_get_version(ctx, &(spdat->version));
202     if(res != FKO_SUCCESS)
203         return(res);
204
205     res = fko_get_spa_message_type(ctx, &(spdat->message_type));
206     if(res != FKO_SUCCESS)
207         return(res);
208
209     res = fko_get_spa_message(ctx, &(spdat->spa_message));
210     if(res != FKO_SUCCESS)
211         return(res);
212
213     res = fko_get_spa_nat_access(ctx, &(spdat->nat_access));
214     if(res != FKO_SUCCESS)
215         return(res);
216
217     res = fko_get_spa_server_auth(ctx, &(spdat->server_auth));
218     if(res != FKO_SUCCESS)
219         return(res);
220
221     res = fko_get_spa_client_timeout(ctx, (int *)&(spdat->client_timeout));
222     if(res != FKO_SUCCESS)
223         return(res);
224
225     return(res);
226 }
227
228 /* Check for access.conf stanza SOURCE match based on SPA packet
229  * source IP
230 */
231 static int
232 is_src_match(acc_stanza_t *acc, const uint32_t ip)
233 {
234     while (acc)
235     {
236         if(compare_addr_list(acc->source_list, ip))
237             return 1;
238
239         acc = acc->next;
240     }
241     return 0;
242 }
243
244 /* Process the SPA packet data
245 */
246 void
247 incoming_spa(fko_srv_options_t *opts)
248 {
249     /* Always a good idea to initialize ctx to null if it will be used
250      * repeatedly (especially when using fko_new_with_data()).
251     */
252     fko_ctx_t       ctx = NULL;
253
254     char            *spa_ip_demark, *gpg_id, *raw_digest = NULL;
255     time_t          now_ts;
256     int             res, status, ts_diff, enc_type, stanza_num=0;
257     int             added_replay_digest = 0;
258
259     spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
260
261     /* This will hold our pertinent SPA data.
262     */
263     spa_data_t spadat;
264
265     /* Loop through all access stanzas looking for a match
266     */
267     acc_stanza_t    *acc = opts->acc_stanzas;
268
269     inet_ntop(AF_INET, &(spa_pkt->packet_src_ip),
270         spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip));
271
272     /* At this point, we want to validate and (if needed) preprocess the
273      * SPA data and/or to be reasonably sure we have a SPA packet (i.e
274      * try to eliminate obvious non-spa packets).
275     */
276     res = preprocess_spa_data(opts, spadat.pkt_source_ip);
277     if(res != FKO_SUCCESS)
278     {
279         if(opts->verbose > 1)
280             log_msg(LOG_INFO, "preprocess_spa_data() returned error %i: '%s' for incoming packet.",
281                 res, get_errstr(res));
282         return;
283     }
284
285     if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip)))
286     {
287         if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
288             /* Check for a replay attack
289             */
290             res = get_raw_digest(&raw_digest, (char *)spa_pkt->packet_data);
291             if(res != FKO_SUCCESS)
292             {
293                 if (raw_digest != NULL)
294                     free(raw_digest);
295                 return;
296             }
297             if (raw_digest == NULL)
298                 return;
299
300             if (is_replay(opts, raw_digest) != SPA_MSG_SUCCESS)
301             {
302                 free(raw_digest);
303                 return;
304             }
305     }
306     else
307     {
308         log_msg(LOG_WARNING,
309             "No access data found for source IP: %s", spadat.pkt_source_ip
310         );
311         return;
312     }
313
314     /* Now that we know there is a matching access.conf stanza and the
315      * incoming SPA packet is not a replay, see if we should grant any
316      * access
317     */
318     while(acc)
319     {
320         stanza_num++;
321
322         /* Check for a match for the SPA source IP and the access stanza
323         */
324         if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)))
325         {
326             acc = acc->next;
327             continue;
328         }
329
330         log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match",
331             stanza_num, spadat.pkt_source_ip);
332
333         if(opts->verbose > 1)
334             log_msg(LOG_INFO, "SPA Packet: '%s'\n", spa_pkt->packet_data);
335
336         /* Make sure this access stanza has not expired
337         */
338         if(acc->access_expire_time > 0)
339         {
340             if(acc->expired)
341             {
342                 acc = acc->next;
343                 continue;
344             }
345             else
346             {
347                 if(time(NULL) > acc->access_expire_time)
348                 {
349                     log_msg(LOG_INFO, "(stanza #%d) Access stanza has expired",
350                         stanza_num);
351                     acc->expired = 1;
352                     acc = acc->next;
353                     continue;
354                 }
355             }
356         }
357
358         /* Get encryption type and try its decoding routine first (if the key
359          * for that type is set)
360         */
361         enc_type = fko_encryption_type((char *)spa_pkt->packet_data);
362
363         if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
364         {
365             if(acc->key != NULL)
366                 res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key);
367             else
368             {
369                 log_msg(LOG_ERR,
370                     "(stanza #%d) No KEY for RIJNDAEL encrypted messages",
371                     stanza_num
372                 );
373                 acc = acc->next;
374                 continue;
375             }
376         }
377         else if(enc_type == FKO_ENCRYPTION_GPG)
378         {
379             /* For GPG we create the new context without decrypting on the fly
380              * so we can set some  GPG parameters first.
381             */
382             if(acc->gpg_decrypt_pw != NULL)
383             {
384                 res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL);
385                 if(res != FKO_SUCCESS)
386                 {
387                     log_msg(LOG_WARNING,
388                         "(stanza #%d) Error creating fko context (before decryption): %s",
389                         stanza_num, fko_errstr(res)
390                     );
391                     acc = acc->next;
392                     continue;
393                 }
394
395                 /* Set whatever GPG parameters we have.
396                 */
397                 if(acc->gpg_home_dir != NULL)
398                     res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir);
399                     if(res != FKO_SUCCESS)
400                     {
401                         log_msg(LOG_WARNING,
402                             "(stanza #%d) Error setting GPG keyring path to %s: %s",
403                             stanza_num, acc->gpg_home_dir, fko_errstr(res)
404                         );
405                         acc = acc->next;
406                         continue;
407                     }
408
409                 if(acc->gpg_decrypt_id != NULL)
410                     fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id);
411
412                 /* If GPG_REQUIRE_SIG is set for this acc stanza, then set
413                  * the FKO context accordingly and check the other GPG Sig-
414                  * related parameters. This also applies when REMOTE_ID is
415                  * set.
416                 */
417                 if(acc->gpg_require_sig)
418                 {
419                     fko_set_gpg_signature_verify(ctx, 1);
420
421                     /* Set whether or not to ignore signature verification errors.
422                     */
423                     fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error);
424                 }
425                 else
426                 {
427                     fko_set_gpg_signature_verify(ctx, 0);
428                     fko_set_gpg_ignore_verify_error(ctx, 1);
429                 }
430
431                 /* Now decrypt the data.
432                 */
433                 res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw);
434
435             }
436             else
437             {
438                 log_msg(LOG_ERR,
439                     "(stanza #%d) No GPG_DECRYPT_PW for GPG encrypted messages, set GPG_ALLOW_NO_PW",
440                     stanza_num
441                 );
442                 acc = acc->next;
443                 continue;
444             }
445         }
446         else
447         {
448             log_msg(LOG_ERR, "(stanza #%d) Unable to determing encryption type. Got type=%i.",
449                 stanza_num, enc_type);
450             acc = acc->next;
451             continue;
452         }
453
454         /* Do we have a valid FKO context?  Did the SPA decrypt properly?
455         */
456         if(res != FKO_SUCCESS)
457         {
458             log_msg(LOG_WARNING, "(stanza #%d) Error creating fko context: %s",
459                 stanza_num, fko_errstr(res));
460
461             if(IS_GPG_ERROR(res))
462                 log_msg(LOG_WARNING, "(stanza #%d) - GPG ERROR: %s",
463                     stanza_num, fko_gpg_errstr(ctx));
464
465             if(ctx != NULL)
466                 fko_destroy(ctx);
467             acc = acc->next;
468             continue;
469         }
470
471         /* Add this SPA packet into the replay detection cache
472         */
473         if (! added_replay_digest)
474         {
475             res = add_replay(opts, raw_digest);
476             if (res != SPA_MSG_SUCCESS)
477             {
478                 log_msg(LOG_WARNING, "(stanza #%d) Could not add digest to replay cache",
479                     stanza_num);
480                 if(ctx != NULL)
481                     fko_destroy(ctx);
482                 acc = acc->next;
483                 continue;
484             }
485             added_replay_digest = 1;
486         }
487
488         /* At this point, we assume the SPA data is valid.  Now we need to see
489          * if it meets our access criteria.
490         */
491         if(opts->verbose > 1)
492             log_msg(LOG_INFO, "(stanza #%d) SPA Decode (res=%i):\n%s",
493                 stanza_num, res, dump_ctx(ctx));
494
495         /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty,
496          * then we need to make sure this incoming message is signer ID matches
497          * an entry in the list.
498         */
499         if(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig)
500         {
501             res = fko_get_gpg_signature_id(ctx, &gpg_id);
502             if(res != FKO_SUCCESS)
503             {
504                 log_msg(LOG_WARNING, "(stanza #%d) Error pulling the GPG signature ID from the context: %s",
505                     stanza_num, fko_gpg_errstr(ctx));
506                 if(ctx != NULL)
507                     fko_destroy(ctx);
508                 acc = acc->next;
509                 continue;
510             }
511
512             if(opts->verbose)
513                 log_msg(LOG_INFO, "(stanza #%d) Incoming SPA data signed by '%s'.",
514                     stanza_num, gpg_id);
515
516             if(acc->gpg_remote_id != NULL && !acc_check_gpg_remote_id(acc, gpg_id))
517             {
518                 log_msg(LOG_WARNING,
519                     "(stanza #%d) Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.",
520                     stanza_num, gpg_id);
521                 if(ctx != NULL)
522                     fko_destroy(ctx);
523                 acc = acc->next;
524                 continue;
525             }
526         }
527
528         /* Populate our spa data struct for future reference.
529         */
530         res = get_spa_data_fields(ctx, &spadat);
531
532         /* Figure out what our timeout will be. If it is specified in the SPA
533          * data, then use that.  If not, try the FW_ACCESS_TIMEOUT from the
534          * access.conf file (if there is one).  Otherwise use the default.
535         */
536         if(spadat.client_timeout > 0)
537             spadat.fw_access_timeout = spadat.client_timeout;
538         else if(acc->fw_access_timeout > 0)
539             spadat.fw_access_timeout = acc->fw_access_timeout;
540         else
541             spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;
542
543         if(res != FKO_SUCCESS)
544         {
545             log_msg(LOG_ERR, "(stanza #%d) Unexpected error pulling SPA data from the context: %s",
546                 stanza_num, fko_errstr(res));
547
548             if(ctx != NULL)
549                 fko_destroy(ctx);
550             acc = acc->next;
551             continue;
552         }
553
554         /* Check packet age if so configured.
555         */
556         if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
557         {
558             time(&now_ts);
559
560             ts_diff = abs(now_ts - spadat.timestamp);
561
562             if(ts_diff > atoi(opts->config[CONF_MAX_SPA_PACKET_AGE]))
563             {
564                 log_msg(LOG_WARNING, "(stanza #%d) SPA data time difference is too great (%i seconds).",
565                     stanza_num, ts_diff);
566
567                 if(ctx != NULL)
568                     fko_destroy(ctx);
569                 acc = acc->next;
570                 continue;
571             }
572         }
573
574         /* At this point, we have enough to check the embedded (or packet source)
575          * IP address against the defined access rights.  We start by splitting
576          * the spa msg source IP from the remainder of the message.
577         */
578         spa_ip_demark = strchr(spadat.spa_message, ',');
579         if(spa_ip_demark == NULL)
580         {
581             log_msg(LOG_WARNING, "(stanza #%d) Error parsing SPA message string: %s",
582                 stanza_num, fko_errstr(res));
583
584             if(ctx != NULL)
585                 fko_destroy(ctx);
586             acc = acc->next;
587             continue;
588         }
589
590         strlcpy(spadat.spa_message_src_ip,
591             spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1);
592
593         if(strnlen(spadat.spa_message_src_ip,
594                 MIN_IPV4_STR_LEN) < MIN_IPV4_STR_LEN)
595         {
596             log_msg(LOG_WARNING, "(stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
597                 stanza_num, fko_errstr(res));
598
599             if(ctx != NULL)
600                 fko_destroy(ctx);
601             acc = acc->next;
602             break;
603         }
604
605         strlcpy(spadat.spa_message_remain, spa_ip_demark+1, 1024);
606
607         /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it
608          * is allowed.
609         */
610         if(strcmp(spadat.spa_message_src_ip, "0.0.0.0") == 0)
611         {
612             if(acc->require_source_address)
613             {
614                 log_msg(LOG_WARNING,
615                     "(stanza #%d) Got 0.0.0.0 when valid source IP was required.",
616                     stanza_num
617                 );
618
619                 if(ctx != NULL)
620                     fko_destroy(ctx);
621                 acc = acc->next;
622                 continue;
623             }
624
625             spadat.use_src_ip = spadat.pkt_source_ip;
626         }
627         else
628             spadat.use_src_ip = spadat.spa_message_src_ip;
629
630         /* If REQUIRE_USERNAME is set, make sure the username in this SPA data
631          * matches.
632         */
633         if(acc->require_username != NULL)
634         {
635             if(strcmp(spadat.username, acc->require_username) != 0)
636             {
637                 log_msg(LOG_WARNING,
638                     "(stanza #%d) Username in SPA data (%s) does not match required username: %s",
639                     stanza_num, spadat.username, acc->require_username
640                 );
641
642                 if(ctx != NULL)
643                     fko_destroy(ctx);
644                 acc = acc->next;
645                 continue;
646             }
647         }
648
649         /* Take action based on SPA message type.
650         */
651         if(spadat.message_type == FKO_LOCAL_NAT_ACCESS_MSG
652               || spadat.message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG
653               || spadat.message_type == FKO_NAT_ACCESS_MSG
654               || spadat.message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG)
655         {
656 #if FIREWALL_IPTABLES
657             if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)!=0)
658             {
659                 log_msg(LOG_WARNING,
660                     "(stanza #%d) SPA packet from %s requested NAT access, but is not enabled",
661                     stanza_num, spadat.pkt_source_ip
662                 );
663
664                 if(ctx != NULL)
665                     fko_destroy(ctx);
666                 acc = acc->next;
667                 continue;
668             }
669 #else
670             log_msg(LOG_WARNING,
671                 "(stanza #%d) SPA packet from %s requested unsupported NAT access",
672                 stanza_num, spadat.pkt_source_ip
673             );
674
675             if(ctx != NULL)
676                 fko_destroy(ctx);
677             acc = acc->next;
678             continue;
679 #endif
680         }
681
682         /* Command messages.
683         */
684         if(spadat.message_type == FKO_COMMAND_MSG)
685         {
686             if(!acc->enable_cmd_exec)
687             {
688                 log_msg(LOG_WARNING,
689                     "(stanza #%d) SPA Command message are not allowed in the current configuration.",
690                     stanza_num
691                 );
692
693                 if(ctx != NULL)
694                     fko_destroy(ctx);
695                 acc = acc->next;
696                 continue;
697             }
698             else
699             {
700                 log_msg(LOG_INFO,
701                     "(stanza #%d) Processing SPA Command message: command='%s'.",
702                     stanza_num, spadat.spa_message_remain
703                 );
704
705                 /* Do we need to become another user? If so, we call
706                  * run_extcmd_as and pass the cmd_exec_uid.
707                 */
708                 if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0)
709                 {
710                     if(opts->verbose)
711                         log_msg(LOG_INFO, "(stanza #%d) Setting effective user to %s (UID=%i) before running command.",
712                             stanza_num, acc->cmd_exec_user, acc->cmd_exec_uid);
713
714
715                     res = run_extcmd_as(acc->cmd_exec_uid,
716                                         spadat.spa_message_remain, NULL, 0, 0);
717                 }
718                 else /* Just run it as we are (root that is). */
719                     res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5);
720
721                 /* --DSS XXX: I have found that the status (and res for that
722                  *            matter) have been unreliable indicators of the
723                  *            actual exit status of some commands.  Not sure
724                  *            why yet.  For now, we will take what we get.
725                 */
726                 status = WEXITSTATUS(res);
727
728                 if(opts->verbose > 1)
729                     log_msg(LOG_WARNING,
730                         "(stanza #%d) CMD_EXEC: command returned %i",
731                         stanza_num, status);
732
733                 if(status != 0)
734                     res = SPA_MSG_COMMAND_ERROR;
735
736                 if(ctx != NULL)
737                     fko_destroy(ctx);
738
739                 /* we processed the command on a matching access stanza, so we
740                  * don't look for anything else to do with this SPA packet
741                 */
742                 break;
743             }
744         }
745
746         /* From this point forward, we have some kind of access message. So
747          * we first see if access is allowed by checking access against
748          * restrict_ports and open_ports.
749          *
750          *  --DSS TODO: We should add BLACKLIST support here as well.
751         */
752         if(! acc_check_port_access(acc, spadat.spa_message_remain))
753         {
754             log_msg(LOG_WARNING,
755                 "(stanza #%d) One or more requested protocol/ports was denied per access.conf.",
756                 stanza_num
757             );
758
759             if(ctx != NULL)
760                 fko_destroy(ctx);
761             acc = acc->next;
762             continue;
763         }
764
765         /* At this point, we process the SPA request and break out of the
766          * access stanza loop (first valid access stanza stops us looking
767          * for others).
768         */
769         process_spa_request(opts, acc, &spadat);
770         if(ctx != NULL)
771             fko_destroy(ctx);
772         break;
773     }
774
775     if (raw_digest != NULL)
776         free(raw_digest);
777
778     return;
779 }
780
781 /***EOF***/