Use symbolic user/group names.
[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     SPConfig& conf=SPConfig::getConfig();
114     if (preinit) {
115
116         // Initialize the SP library.
117         conf.setFeatures(
118             SPConfig::Listener |
119             SPConfig::Caching |
120             SPConfig::Metadata |
121             SPConfig::Trust |
122             SPConfig::Credentials |
123             SPConfig::AttributeResolution |
124             SPConfig::Handlers |
125             SPConfig::OutOfProcess |
126             (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)
127             );
128         if (!conf.init(shar_schemadir, shar_prefix)) {
129             fprintf(stderr, "configuration is invalid, see console for specific problems\n");
130             return -1;
131         }
132
133         if (!conf.instantiate(shar_config)) {
134             fprintf(stderr, "configuration is invalid, check console for specific problems\n");
135             conf.term();
136             return -2;
137         }
138
139         // If just a test run, bail.
140         if (shar_checkonly) {
141             fprintf(stdout, "overall configuration is loadable, check console for non-fatal problems\n");
142             return 0;
143         }
144     }
145     else {
146
147         //_CrtSetAllocHook(MyAllocHook);
148
149         if (!shar_checkonly) {
150             // Run the listener.
151             ListenerService* listener = conf.getServiceProvider()->getListenerService();
152             if (!listener->init(unlink_socket)) {
153                 fprintf(stderr, "listener failed to initialize\n");
154                 conf.term();
155                 return -3;
156             }
157             else if (!listener->run(&shibd_shutdown)) {
158                 fprintf(stderr, "listener failed during service\n");
159                 listener->term();
160                 conf.term();
161                 return -3;
162             }
163             listener->term();
164         }
165
166         conf.term();
167     }
168     return 0;
169 }
170
171 #else
172
173 int daemon_wait = 3;
174 bool shibd_running = false;
175 bool daemonize = true;
176 const char* runasuser = nullptr;
177 const char* runasgroup = nullptr;
178
179 static void term_handler(int arg)
180 {
181     shibd_shutdown = true;
182 }
183
184 static void run_handler(int arg)
185 {
186     shibd_running = true;
187 }
188
189 static void child_handler(int arg)
190 {
191     // Terminate the parent's wait/sleep if the newly born daemon dies early.
192 }
193
194 static int setup_signals(void)
195 {
196     struct sigaction sa;
197     memset(&sa, 0, sizeof (sa));
198     sa.sa_handler = SIG_IGN;
199     sa.sa_flags = SA_RESTART;
200
201     if (sigaction(SIGPIPE, &sa, nullptr) < 0) {
202         return -1;
203     }
204
205     memset(&sa, 0, sizeof (sa));
206     sa.sa_handler = term_handler;
207     sa.sa_flags = SA_RESTART;
208
209     if (sigaction(SIGHUP, &sa, nullptr) < 0) {
210         return -1;
211     }
212     if (sigaction(SIGINT, &sa, nullptr) < 0) {
213         return -1;
214     }
215     if (sigaction(SIGQUIT, &sa, nullptr) < 0) {
216         return -1;
217     }
218     if (sigaction(SIGTERM, &sa, nullptr) < 0) {
219         return -1;
220     }
221
222     if (daemonize) {
223         memset(&sa, 0, sizeof (sa));
224         sa.sa_handler = run_handler;
225
226         if (sigaction(SIGUSR1, &sa, nullptr) < 0) {
227             return -1;
228         }
229
230         memset(&sa, 0, sizeof (sa));
231         sa.sa_handler = child_handler;
232
233         if (sigaction(SIGCHLD, &sa, nullptr) < 0) {
234             return -1;
235         }
236     }
237
238     return 0;
239 }
240
241 static void usage(char* whoami)
242 {
243     fprintf(stderr, "usage: %s [-dcxtfFpwugvh]\n", whoami);
244     fprintf(stderr, "  -d\tinstallation prefix to use\n");
245     fprintf(stderr, "  -c\tconfig file to use\n");
246     fprintf(stderr, "  -x\tXML schema catalogs to use\n");
247     fprintf(stderr, "  -t\ttest configuration file for problems\n");
248     fprintf(stderr, "  -f\tforce removal of listener socket\n");
249     fprintf(stderr, "  -F\tstay in the foreground\n");
250     fprintf(stderr, "  -p\tpid file to use\n");
251     fprintf(stderr, "  -w\tseconds to wait for successful daemonization\n");
252     fprintf(stderr, "  -u\tuser to run under\n");
253     fprintf(stderr, "  -g\tgroup to run under\n");
254     fprintf(stderr, "  -v\tprint software version\n");
255     fprintf(stderr, "  -h\tprint this help message\n");
256     exit(1);
257 }
258
259 static int parse_args(int argc, char* argv[])
260 {
261     int opt;
262
263     while ((opt = getopt(argc, argv, "d:c:x:p:w:u:g:fFtvh")) > 0) {
264         switch (opt) {
265             case 'd':
266                 shar_prefix=optarg;
267                 break;
268             case 'c':
269                 shar_config=optarg;
270                 break;
271             case 'x':
272                 shar_schemadir=optarg;
273                 break;
274             case 'f':
275                 unlink_socket = true;
276                 break;
277             case 'F':
278                 daemonize = false;
279                 break;
280             case 't':
281                 shar_checkonly=true;
282                 daemonize=false;
283                 break;
284             case 'v':
285                 shar_version=true;
286                 break;
287             case 'p':
288                 pidfile=optarg;
289                 break;
290             case 'w':
291                 if (optarg)
292                     daemon_wait = atoi(optarg);
293                 if (daemon_wait <= 0)
294                     daemon_wait = 3;
295                 break;
296             case 'u':
297                 if (optarg)
298                     runasuser = optarg;
299                 break;
300             case 'g':
301                 if (optarg)
302                     runasgroup = optarg;
303                 break;
304             default:
305                 return -1;
306         }
307     }
308     return 0;
309 }
310
311 int main(int argc, char *argv[])
312 {
313     if (parse_args(argc, argv) != 0)
314         usage(argv[0]);
315     else if (shar_version) {
316         fprintf(stdout, PACKAGE_STRING"\n");
317         return 0;
318     }
319
320     if (setup_signals() != 0)
321         return -1;
322
323     if (runasgroup) {
324 #ifdef HAVE_GETGRNAM
325         struct group* grp = getgrnam(runasgroup);
326         if (!grp) {
327             fprintf(stderr, "getgrnam failed, check -g option\n");
328             return -1;
329         }
330         if (setgid(grp->gr_gid) != 0) {
331             fprintf(stderr, "setgid failed, check -g option\n");
332             return -1;
333         }
334 #else
335         fprintf(stderr, "-g not supported on this platform");
336         return -1;
337 #endif
338     }
339
340     if (runasuser) {
341 #ifdef HAVE_GETPWNAM
342         struct passwd* pwd = getpwnam(runasuser);
343         if (!pwd) {
344             fprintf(stderr, "getpwnam failed, check -u option\n");
345             return -1;
346         }
347         if (setuid(pwd->pw_uid) != 0) {
348             fprintf(stderr, "setuid failed, check -u option\n");
349             return -1;
350         }
351 #else
352         fprintf(stderr, "-u not supported on this platform");
353         return -1;
354 #endif
355     }
356
357     // initialize the shib-target library
358     SPConfig& conf=SPConfig::getConfig();
359     conf.setFeatures(
360         SPConfig::Listener |
361         SPConfig::Caching |
362         SPConfig::Metadata |
363         SPConfig::Trust |
364         SPConfig::Credentials |
365         SPConfig::AttributeResolution |
366         SPConfig::Handlers |
367         SPConfig::OutOfProcess |
368         (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)
369         );
370     if (!conf.init(shar_schemadir, shar_prefix)) {
371         fprintf(stderr, "configuration is invalid, check console for specific problems\n");
372         return -1;
373     }
374
375     if (daemonize) {
376         // We must fork() early, while we're single threaded.
377         // StorageService cleanup thread is about to start.
378         switch (fork()) {
379             case 0:
380                 break;
381             case -1:
382                 perror("forking");
383                 exit(EXIT_FAILURE);
384             default:
385                 sleep(daemon_wait);
386                 exit(shibd_running ? EXIT_SUCCESS : EXIT_FAILURE);
387         }
388     }
389
390     if (!conf.instantiate(shar_config)) {
391         fprintf(stderr, "configuration is invalid, check console for specific problems\n");
392         conf.term();
393         return -2;
394     }
395
396     if (shar_checkonly)
397         fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");
398     else {
399         // Init the listener.
400         ListenerService* listener = conf.getServiceProvider()->getListenerService();
401         if (!listener->init(unlink_socket)) {
402             fprintf(stderr, "listener failed to initialize\n");
403             conf.term();
404             return -3;
405         }
406
407         if (daemonize) {
408             if (setsid() == -1) {
409                 perror("setsid");
410                 exit(EXIT_FAILURE);
411             }
412             if (chdir("/") == -1) {
413                 perror("chdir to root");
414                 exit(EXIT_FAILURE);
415             }
416
417             if (pidfile) {
418                 FILE* pidf = fopen(pidfile, "w");
419                 if (pidf) {
420                     fprintf(pidf, "%d\n", getpid());
421                     fclose(pidf);
422                 }
423                 else {
424                     perror(pidfile);
425                 }
426             }
427
428             freopen("/dev/null", "r", stdin);
429             freopen("/dev/null", "w", stdout);
430             freopen("/dev/null", "w", stderr);
431
432             // Signal our parent that we are A-OK.
433             kill(getppid(), SIGUSR1);
434         }
435
436         // Run the listener.
437         if (!listener->run(&shibd_shutdown)) {
438             fprintf(stderr, "listener failure during service\n");
439             listener->term();
440             conf.term();
441             if (daemonize && pidfile)
442                 unlink(pidfile);
443             return -3;
444         }
445         listener->term();
446     }
447
448     conf.term();
449     if (daemonize && pidfile)
450         unlink(pidfile);
451     return 0;
452 }
453
454 #endif