8c67563a239cdc05c12c4b130370140314716a70
[shibboleth/cpp-sp.git] / shibd / shibd.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /*
22  * shibd.cpp -- the shibd "main" code.
23  */
24
25
26 // eventually we might be able to support autoconf via cygwin...
27 #if defined (_MSC_VER) || defined(__BORLANDC__)
28 # include "config_win32.h"
29 #else
30 # include "config.h"
31 #endif
32
33 #ifdef WIN32
34 # define _CRT_NONSTDC_NO_DEPRECATE 1
35 # define _CRT_SECURE_NO_DEPRECATE 1
36 #endif
37
38 #include <shibsp/SPConfig.h>
39
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 # include <sys/select.h>
43 #endif
44
45 #if defined(HAVE_GRP_H) && defined(HAVE_PWD_H)
46 # include <pwd.h>
47 # include <grp.h>
48 #endif
49
50 #include <stdio.h>
51 #include <signal.h>
52 #include <shibsp/ServiceProvider.h>
53 #include <shibsp/remoting/ListenerService.h>
54 #include <xercesc/util/XMLUniDefs.hpp>
55 #include <xmltooling/XMLToolingConfig.h>
56 #include <xmltooling/util/XMLConstants.h>
57 #include <xmltooling/util/XMLHelper.h>
58
59 using namespace shibsp;
60 using namespace xmltooling;
61 using namespace std;
62
63 bool shibd_shutdown = false;
64 const char* shar_config = nullptr;
65 const char* shar_schemadir = nullptr;
66 const char* shar_prefix = nullptr;
67 bool shar_checkonly = false;
68 bool shar_version = false;
69 static bool unlink_socket = false;
70 const char* pidfile = nullptr;
71
72 #ifdef WIN32
73
74 //#include <CRTDBG.H>
75
76 #define nNoMansLandSize 4
77 typedef struct _CrtMemBlockHeader
78 {
79         struct _CrtMemBlockHeader * pBlockHeaderNext;
80         struct _CrtMemBlockHeader * pBlockHeaderPrev;
81         char *                      szFileName;
82         int                         nLine;
83         size_t                      nDataSize;
84         int                         nBlockUse;
85         long                        lRequest;
86         unsigned char               gap[nNoMansLandSize];
87         /* followed by:
88          *  unsigned char           data[nDataSize];
89          *  unsigned char           anotherGap[nNoMansLandSize];
90          */
91 } _CrtMemBlockHeader;
92
93 /*
94 int MyAllocHook(int nAllocType, void *pvData,
95       size_t nSize, int nBlockUse, long lRequest,
96       const unsigned char * szFileName, int nLine)
97 {
98     if ( nBlockUse == _CRT_BLOCK )
99       return( TRUE );
100     if (nAllocType == _HOOK_FREE) {
101         _CrtMemBlockHeader* ptr = (_CrtMemBlockHeader*)(((_CrtMemBlockHeader *)pvData)-1);
102         if (ptr->nDataSize == 8192)
103             fprintf(stderr,"free  request %u size %u\n", ptr->lRequest, ptr->nDataSize);
104     }
105     else if (nAllocType == _HOOK_ALLOC && nSize == 8192)
106         fprintf(stderr,"%s request %u size %u\n", ((nAllocType == _HOOK_ALLOC) ? "alloc" : "realloc"), lRequest, nSize);
107     return (TRUE);
108 }
109 */
110
111 int real_main(int preinit)
112 {
113     if (shar_version) {
114         if (preinit)
115             fprintf(stdout, PACKAGE_STRING"\n");
116         return 0;
117     }
118
119     SPConfig& conf = SPConfig::getConfig();
120     if (preinit) {
121         // Initialize the SP library.
122         conf.setFeatures(
123             SPConfig::Listener |
124             SPConfig::Caching |
125             SPConfig::Metadata |
126             SPConfig::Trust |
127             SPConfig::Credentials |
128             SPConfig::AttributeResolution |
129             SPConfig::Handlers |
130             SPConfig::OutOfProcess |
131             (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)
132             );
133         if (!conf.init(shar_schemadir, shar_prefix)) {
134             fprintf(stderr, "configuration is invalid, see console for specific problems\n");
135             return -1;
136         }
137
138         if (!conf.instantiate(shar_config)) {
139             fprintf(stderr, "configuration is invalid, check console for specific problems\n");
140             conf.term();
141             return -2;
142         }
143
144         // If just a test run, bail.
145         if (shar_checkonly) {
146             fprintf(stdout, "overall configuration is loadable, check console for non-fatal problems\n");
147             return 0;
148         }
149     }
150     else {
151
152         //_CrtSetAllocHook(MyAllocHook);
153
154         if (!shar_checkonly) {
155             // Run the listener.
156             ListenerService* listener = conf.getServiceProvider()->getListenerService();
157             if (!listener->init(unlink_socket)) {
158                 fprintf(stderr, "listener failed to initialize\n");
159                 conf.term();
160                 return -3;
161             }
162             else if (!listener->run(&shibd_shutdown)) {
163                 fprintf(stderr, "listener failed during service\n");
164                 listener->term();
165                 conf.term();
166                 return -3;
167             }
168             listener->term();
169         }
170
171         conf.term();
172     }
173     return 0;
174 }
175
176 #else
177
178 int daemon_wait = 3;
179 bool shibd_running = false;
180 bool daemonize = true;
181 const char* runasuser = nullptr;
182 const char* runasgroup = nullptr;
183
184 static void term_handler(int arg)
185 {
186     shibd_shutdown = true;
187 }
188
189 static void run_handler(int arg)
190 {
191     shibd_running = true;
192 }
193
194 static void child_handler(int arg)
195 {
196     // Terminate the parent's wait/sleep if the newly born daemon dies early.
197 }
198
199 static int setup_signals(void)
200 {
201     struct sigaction sa;
202     memset(&sa, 0, sizeof (sa));
203     sa.sa_handler = SIG_IGN;
204     sa.sa_flags = SA_RESTART;
205
206     if (sigaction(SIGPIPE, &sa, nullptr) < 0) {
207         return -1;
208     }
209
210     memset(&sa, 0, sizeof (sa));
211     sa.sa_handler = term_handler;
212     sa.sa_flags = SA_RESTART;
213
214     if (sigaction(SIGHUP, &sa, nullptr) < 0) {
215         return -1;
216     }
217     if (sigaction(SIGINT, &sa, nullptr) < 0) {
218         return -1;
219     }
220     if (sigaction(SIGQUIT, &sa, nullptr) < 0) {
221         return -1;
222     }
223     if (sigaction(SIGTERM, &sa, nullptr) < 0) {
224         return -1;
225     }
226
227     if (daemonize) {
228         memset(&sa, 0, sizeof (sa));
229         sa.sa_handler = run_handler;
230
231         if (sigaction(SIGUSR1, &sa, nullptr) < 0) {
232             return -1;
233         }
234
235         memset(&sa, 0, sizeof (sa));
236         sa.sa_handler = child_handler;
237
238         if (sigaction(SIGCHLD, &sa, nullptr) < 0) {
239             return -1;
240         }
241     }
242
243     return 0;
244 }
245
246 static void usage(char* whoami)
247 {
248     fprintf(stderr, "usage: %s [-dcxtfFpwugvh]\n", whoami);
249     fprintf(stderr, "  -d\tinstallation prefix to use\n");
250     fprintf(stderr, "  -c\tconfig file to use\n");
251     fprintf(stderr, "  -x\tXML schema catalogs to use\n");
252     fprintf(stderr, "  -t\ttest configuration file for problems\n");
253     fprintf(stderr, "  -f\tforce removal of listener socket\n");
254     fprintf(stderr, "  -F\tstay in the foreground\n");
255     fprintf(stderr, "  -p\tpid file to use\n");
256     fprintf(stderr, "  -w\tseconds to wait for successful daemonization\n");
257     fprintf(stderr, "  -u\tuser to run under\n");
258     fprintf(stderr, "  -g\tgroup to run under\n");
259     fprintf(stderr, "  -v\tprint software version\n");
260     fprintf(stderr, "  -h\tprint this help message\n");
261     exit(1);
262 }
263
264 static int parse_args(int argc, char* argv[])
265 {
266     int opt;
267
268     while ((opt = getopt(argc, argv, "d:c:x:p:w:u:g:fFtvh")) > 0) {
269         switch (opt) {
270             case 'd':
271                 shar_prefix=optarg;
272                 break;
273             case 'c':
274                 shar_config=optarg;
275                 break;
276             case 'x':
277                 shar_schemadir=optarg;
278                 break;
279             case 'f':
280                 unlink_socket = true;
281                 break;
282             case 'F':
283                 daemonize = false;
284                 break;
285             case 't':
286                 shar_checkonly=true;
287                 daemonize=false;
288                 break;
289             case 'v':
290                 shar_version=true;
291                 break;
292             case 'p':
293                 pidfile=optarg;
294                 break;
295             case 'w':
296                 if (optarg)
297                     daemon_wait = atoi(optarg);
298                 if (daemon_wait <= 0)
299                     daemon_wait = 3;
300                 break;
301             case 'u':
302                 if (optarg)
303                     runasuser = optarg;
304                 break;
305             case 'g':
306                 if (optarg)
307                     runasgroup = optarg;
308                 break;
309             default:
310                 return -1;
311         }
312     }
313     return 0;
314 }
315
316 int main(int argc, char *argv[])
317 {
318     if (parse_args(argc, argv) != 0)
319         usage(argv[0]);
320     else if (shar_version) {
321         fprintf(stdout, PACKAGE_STRING"\n");
322         return 0;
323     }
324
325     if (setup_signals() != 0)
326         return -1;
327
328     if (runasgroup) {
329 #ifdef HAVE_GETGRNAM
330         struct group* grp = getgrnam(runasgroup);
331         if (!grp) {
332             fprintf(stderr, "getgrnam failed, check -g option\n");
333             return -1;
334         }
335         if (setgid(grp->gr_gid) != 0) {
336             fprintf(stderr, "setgid failed, check -g option\n");
337             return -1;
338         }
339 #else
340         fprintf(stderr, "-g not supported on this platform");
341         return -1;
342 #endif
343     }
344
345     if (runasuser) {
346 #ifdef HAVE_GETPWNAM
347         struct passwd* pwd = getpwnam(runasuser);
348         if (!pwd) {
349             fprintf(stderr, "getpwnam failed, check -u option\n");
350             return -1;
351         }
352 #ifdef HAVE_INITGROUPS
353         // w/out initgroups/setgroups process retains supplementary groups
354         if (initgroups(pwd->pw_name, pwd->pw_gid) != 0) {
355             fprintf(stderr, "initgroups failed, check -u option\n");
356             return -1;
357         }
358 #endif
359         if (setuid(pwd->pw_uid) != 0) {
360             fprintf(stderr, "setuid failed, check -u option\n");
361             return -1;
362         }
363 #else
364         fprintf(stderr, "-u not supported on this platform");
365         return -1;
366 #endif
367     }
368
369     // initialize the shib-target library
370     SPConfig& conf=SPConfig::getConfig();
371     conf.setFeatures(
372         SPConfig::Listener |
373         SPConfig::Caching |
374         SPConfig::Metadata |
375         SPConfig::Trust |
376         SPConfig::Credentials |
377         SPConfig::AttributeResolution |
378         SPConfig::Handlers |
379         SPConfig::OutOfProcess |
380         (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)
381         );
382     if (!conf.init(shar_schemadir, shar_prefix)) {
383         fprintf(stderr, "configuration is invalid, check console for specific problems\n");
384         return -1;
385     }
386
387     if (daemonize) {
388         // We must fork() early, while we're single threaded.
389         // StorageService cleanup thread is about to start.
390         switch (fork()) {
391             case 0:
392                 break;
393             case -1:
394                 perror("forking");
395                 exit(EXIT_FAILURE);
396             default:
397                 sleep(daemon_wait);
398                 exit(shibd_running ? EXIT_SUCCESS : EXIT_FAILURE);
399         }
400     }
401
402     if (!conf.instantiate(shar_config)) {
403         fprintf(stderr, "configuration is invalid, check console for specific problems\n");
404         conf.term();
405         return -2;
406     }
407
408     if (shar_checkonly)
409         fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");
410     else {
411         // Init the listener.
412         ListenerService* listener = conf.getServiceProvider()->getListenerService();
413         if (!listener->init(unlink_socket)) {
414             fprintf(stderr, "listener failed to initialize\n");
415             conf.term();
416             return -3;
417         }
418
419         if (daemonize) {
420             if (setsid() == -1) {
421                 perror("setsid");
422                 exit(EXIT_FAILURE);
423             }
424             if (chdir("/") == -1) {
425                 perror("chdir to root");
426                 exit(EXIT_FAILURE);
427             }
428
429             if (pidfile) {
430                 FILE* pidf = fopen(pidfile, "w");
431                 if (pidf) {
432                     fprintf(pidf, "%d\n", getpid());
433                     fclose(pidf);
434                 }
435                 else {
436                     perror(pidfile);
437                 }
438             }
439
440             freopen("/dev/null", "r", stdin);
441             freopen("/dev/null", "w", stdout);
442             freopen("/dev/null", "w", stderr);
443
444             // Signal our parent that we are A-OK.
445             kill(getppid(), SIGUSR1);
446         }
447
448         // Run the listener.
449         if (!listener->run(&shibd_shutdown)) {
450             fprintf(stderr, "listener failure during service\n");
451             listener->term();
452             conf.term();
453             if (daemonize && pidfile)
454                 unlink(pidfile);
455             return -3;
456         }
457         listener->term();
458     }
459
460     conf.term();
461     if (daemonize && pidfile)
462         unlink(pidfile);
463     return 0;
464 }
465
466 #endif