2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: security.c,v 1.17.2.6.4.1.2.5.2.1 2004/04/29 20:47:22 martinea Exp $
29 * wrapper file for kerberos security
36 * Change the following from #undef to #define to cause detailed logging
37 * of the security steps, e.g. into /tmp/amanda/amandad*debug.
39 #undef SHOW_SECURITY_DETAIL
42 * If we don't have the new-style wait access functions, use our own,
43 * compatible with old-style BSD systems at least. Note that we don't
44 * care about the case w_stopval == WSTOPPED since we don't ask to see
45 * stopped processes, so should never get them from wait.
48 # define WEXITSTATUS(r) (((union wait *) &(r))->w_retcode)
49 # define WTERMSIG(r) (((union wait *) &(r))->w_termsig)
52 # define WIFSIGNALED(r) (((union wait *) &(r))->w_termsig != 0)
55 #if defined(TEST) /* { */
56 #define SHOW_SECURITY_DETAIL
58 #define dbprintf(p) printf p
61 #if defined(SHOW_SECURITY_DETAIL) /* { */
62 void show_stat_info(a, b)
65 char *name = vstralloc(a, b, NULL);
72 if (stat(name, &sbuf) != 0) {
73 dbprintf(("%s: cannot stat %s: %s\n",
74 debug_prefix_time(NULL), name, strerror(errno)));
78 if ((pwptr = getpwuid(sbuf.st_uid)) == NULL) {
79 owner = alloc(NUM_STR_SIZE);
80 ap_snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_uid);
82 owner = stralloc(pwptr->pw_name);
84 if ((grptr = getgrgid(sbuf.st_gid)) == NULL) {
85 group = alloc(NUM_STR_SIZE);
86 ap_snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_gid);
88 group = stralloc(grptr->gr_name);
90 dbprintf(("%s: processing file: %s\n", debug_prefix_time(NULL), name));
91 dbprintf(("%s: owner=%s group=%s mode=%03o\n",
92 debug_prefix(NULL), owner, group, (int) (sbuf.st_mode & 0777)));
99 #ifdef KRB4_SECURITY /* { */
100 #include "krb4-security.c"
103 int bsd_security_ok P((struct sockaddr_in *addr,
104 char *str, uint32_t cksum, char **errstr));
106 char *get_bsd_security()
108 struct passwd *pwptr;
110 if((pwptr = getpwuid(getuid())) == NULL)
111 error("can't get login name for my uid %ld", (long)getuid());
112 return stralloc2("SECURITY USER ", pwptr->pw_name);
115 int security_ok(addr, str, cksum, errstr)
116 struct sockaddr_in *addr;
121 #ifdef KRB4_SECURITY /* { */
123 return krb4_security_ok(addr, str, cksum, errstr);
126 return bsd_security_ok(addr, str, cksum, errstr);
129 #ifdef BSD_SECURITY /* { */
131 int bsd_security_ok(addr, str, cksum, errstr)
132 struct sockaddr_in *addr;
137 char *remotehost = NULL, *remoteuser = NULL, *localuser = NULL;
138 char *bad_bsd = NULL;
140 struct passwd *pwptr;
144 char number[NUM_STR_SIZE];
145 #ifdef USE_AMANDAHOSTS /* { */
150 int amandahostsauth = 0;
155 amwait_t wait_exitcode;
157 pid_t pid, ruserok_pid;
162 /* what host is making the request? */
164 hp = gethostbyaddr((char *)&addr->sin_addr, (int)sizeof(addr->sin_addr),
167 /* XXX include remote address in message */
168 *errstr = vstralloc("[",
169 "addr ", inet_ntoa(addr->sin_addr), ": ",
170 "hostname lookup failed",
174 remotehost = stralloc(hp->h_name);
176 /* Now let's get the hostent for that hostname */
177 hp = gethostbyname( remotehost );
179 /* XXX include remote hostname in message */
180 *errstr = vstralloc("[",
181 "host ", remotehost, ": ",
182 "hostname lookup failed",
188 /* Verify that the hostnames match -- they should theoretically */
189 if( strncasecmp( remotehost, hp->h_name, strlen(remotehost)+1 ) != 0 ) {
190 *errstr = vstralloc("[",
191 "hostnames do not match: ",
192 remotehost, " ", hp->h_name,
198 /* Now let's verify that the ip which gave us this hostname
199 * is really an ip for this hostname; or is someone trying to
200 * break in? (THIS IS THE CRUCIAL STEP)
202 for (i = 0; hp->h_addr_list[i]; i++) {
203 if (memcmp(hp->h_addr_list[i],
204 (char *) &addr->sin_addr, sizeof(addr->sin_addr)) == 0)
205 break; /* name is good, keep it */
208 /* If we did not find it, your DNS is messed up or someone is trying
209 * to pull a fast one on you. :(
212 /* Check even the aliases list. Work around for Solaris if dns goes over NIS */
214 if( !hp->h_addr_list[i] ) {
215 for (j = 0; hp->h_aliases[j] !=0 ; j++) {
216 if ( strcmp(hp->h_aliases[j],inet_ntoa(addr->sin_addr)) == 0)
217 break; /* name is good, keep it */
219 if( !hp->h_aliases[j] ) {
220 *errstr = vstralloc("[",
221 "ip address ", inet_ntoa(addr->sin_addr),
222 " is not in the ip list for ", remotehost,
230 /* next, make sure the remote port is a "reserved" one */
232 if(ntohs(addr->sin_port) >= IPPORT_RESERVED) {
233 ap_snprintf(number, sizeof(number), "%d", ntohs(addr->sin_port));
234 *errstr = vstralloc("[",
235 "host ", remotehost, ": ",
236 "port ", number, " not secure",
242 /* extract the remote user name from the message */
247 bad_bsd = vstralloc("[",
248 "host ", remotehost, ": ",
249 "bad bsd security line",
253 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
263 skip_whitespace(s, ch);
271 skip_non_whitespace(s, ch);
273 remoteuser = stralloc(fp);
277 /* lookup our local user name */
280 if((pwptr = getpwuid(myuid)) == NULL) {
281 error("error [getpwuid(%d) fails]", myuid);
284 localuser = stralloc(pwptr->pw_name);
286 dbprintf(("%s: bsd security: remote host %s user %s local user %s\n",
287 debug_prefix_time(NULL), remotehost, remoteuser, localuser));
289 #ifndef USE_AMANDAHOSTS /* { */
291 * Note that some versions of ruserok (eg SunOS 3.2) look in
292 * "./.rhosts" rather than "~localuser/.rhosts", so we have to
293 * change directories ourselves. Sigh.
295 * And, believe it or not, some ruserok()'s try an initgroup just
296 * for the hell of it. Since we probably aren't root at this point
297 * it'll fail, and initgroup "helpfully" will blatt "Setgroups: Not owner"
298 * into our stderr output even though the initgroup failure is not a
299 * problem and is expected. Thanks a lot. Not.
302 error("error [pipe() fails]");
304 if ((ruserok_pid = fork ()) < 0) {
305 error("error [fork() fails]");
306 } else if (ruserok_pid == 0) {
308 fError = fdopen(fd[1], "w");
309 /* pamper braindead ruserok's */
310 if(chdir(pwptr->pw_dir) != 0) {
311 fprintf(fError, "[chdir(%s) failed: %s]",
312 pwptr->pw_dir, strerror(errno));
317 #if defined(SHOW_SECURITY_DETAIL) /* { */
319 char *dir = stralloc(pwptr->pw_dir);
321 dbprintf(("%s: calling ruserok(%s, %d, %s, %s)\n",
322 debug_prefix_time(NULL),
323 remotehost, myuid == 0, remoteuser, localuser));
325 dbprintf(("%s: because you are running as root, ",
326 debug_prefix(NULL)));
327 dbprintf(("/etc/hosts.equiv will not be used\n"));
329 show_stat_info("/etc/hosts.equiv", NULL);
331 show_stat_info(dir, "/.rhosts");
336 saved_stderr = dup(2);
338 (void)open("/dev/null", 2);
340 if(ruserok(remotehost, myuid == 0, remoteuser, localuser) == -1) {
341 dup2(saved_stderr,2);
343 *errstr = vstralloc("[",
344 "access as ", localuser, " not allowed",
345 " from ", remoteuser, "@", remotehost,
348 fputs(*errstr, fError);
351 dbprintf(("%s: check failed: %s\n",
352 debug_prefix_time(NULL), *errstr));
357 dup2(saved_stderr,2);
359 dbprintf(("%s: bsd security check to %s from %s@%s passed\n",
360 debug_prefix_time(NULL),
361 localuser, remoteuser, remotehost));
368 fError = fdopen(fd[0], "r");
370 while((pid = wait(&wait_exitcode)) == (pid_t)-1 && errno == EINTR) {}
371 if (pid == (pid_t)-1) {
372 *errstr = vstralloc("[",
373 "access as ", localuser, " not allowed",
374 " from ", remoteuser, "@", remotehost,
379 } else if (pid != ruserok_pid) {
380 ap_snprintf(number, sizeof(number), "%ld", (long)pid);
381 *errstr = vstralloc("[",
382 "access as ", localuser, " not allowed",
383 " from ", remoteuser, "@", remotehost,
388 } else if (WIFSIGNALED(wait_exitcode)) {
389 ap_snprintf(number, sizeof(number), "%d", WTERMSIG(wait_exitcode));
390 *errstr = vstralloc("[",
391 "access as ", localuser, " not allowed",
392 " from ", remoteuser, "@", remotehost,
393 "] got signal ", number,
397 exitcode = WEXITSTATUS(wait_exitcode);
400 if((*errstr = agets(fError)) == NULL) {
401 *errstr = vstralloc("[",
402 "access as ", localuser, " not allowed",
403 " from ", remoteuser, "@", remotehost,
404 "] could not get result",
412 return *errstr == NULL;
414 #if defined(SHOW_SECURITY_DETAIL) /* { */
415 show_stat_info(pwptr->pw_dir, "/.amandahosts");
418 ptmp = stralloc2(pwptr->pw_dir, "/.amandahosts");
419 if((fPerm = fopen(ptmp, "r")) == NULL) {
421 * Put an explanation in the amandad.debug log that will help a
422 * system administrator fix the problem, but don't send a clue
423 * back to the other end to tell them what to fix in order to
424 * be able to hack our system.
426 dbprintf(("%s: fopen of %s failed: %s\n",
427 debug_prefix_time(NULL), ptmp, strerror(errno)));
428 *errstr = vstralloc("[",
429 "access as ", localuser, " not allowed",
430 " from ", remoteuser, "@", remotehost,
442 for(; (pbuf = agets(fPerm)) != NULL; free(pbuf)) {
443 #if defined(SHOW_SECURITY_DETAIL) /* { */
444 dbprintf(("%s: processing line: <%s>\n", debug_prefix(NULL), pbuf));
446 pbuf_len = strlen(pbuf);
450 /* Find end of remote host */
451 skip_non_whitespace(s, ch);
453 memset(pbuf, '\0', pbuf_len); /* leave no trace */
454 continue; /* no remotehost field */
456 s[-1] = '\0'; /* terminate remotehost field */
458 /* Find start of remote user */
459 skip_whitespace(s, ch);
461 ptmp = localuser; /* no remoteuser field */
463 ptmp = s-1; /* start of remoteuser field */
465 /* Find end of remote user */
466 skip_non_whitespace(s, ch);
467 s[-1] = '\0'; /* terminate remoteuser field */
469 #if defined(SHOW_SECURITY_DETAIL) /* { */
470 dbprintf(("%s: comparing %s with\n", debug_prefix(NULL), pbuf));
471 dbprintf(("%s: %s (%s)\n",
472 debug_prefix(NULL), remotehost,
473 (strcasecmp(pbuf, remotehost) == 0) ? "match" : "no match"));
474 dbprintf(("%s: and %s with\n", debug_prefix(NULL), ptmp));
475 dbprintf(("%s: %s (%s)\n",
476 debug_prefix(NULL), remoteuser,
477 (strcasecmp(ptmp, remoteuser) == 0) ? "match" : "no match"));
479 if(strcasecmp(pbuf, remotehost) == 0
480 && strcasecmp(ptmp, remoteuser) == 0) {
484 memset(pbuf, '\0', pbuf_len); /* leave no trace */
489 if(amandahostsauth) {
490 dbprintf(("%s: amandahosts security check passed\n",
491 debug_prefix_time(NULL)));
498 *errstr = vstralloc("[",
499 "access as ", localuser, " not allowed",
500 " from ", remoteuser, "@", remotehost,
501 "] amandahostsauth failed", NULL);
502 dbprintf(("%s: check failed: %s\n", debug_prefix_time(NULL), *errstr));
512 #else /* ! BSD_SECURITY */ /* } { */
514 int bsd_security_ok(addr, str, cksum, errstr)
515 struct sockaddr_in *addr;
520 #if defined(SHOW_SECURITY_DETAIL) /* { */
521 dbprintf(("You configured Amanda using --without-bsd-security, so it\n"));
522 dbprintf(("will let anyone on the Internet connect and do dumps of\n"));
523 dbprintf(("your system unless you have some other kind of protection,\n"));
524 dbprintf(("such as a firewall or TCP wrappers.\n"));
529 #endif /* ! BSD_SECURITY */ /* } */
531 #if defined(TEST) /* { */
539 struct sockaddr_in fake;
543 struct passwd *pwent;
546 * The following is stolen from amandad to emulate what it would
549 if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
550 client_uid = pwent->pw_uid;
551 client_gid = pwent->pw_gid;
556 /* we'd rather not run as root */
559 if(client_uid == (uid_t) -1) {
560 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
564 * if we're using kerberos security, we'll need to be root in
565 * order to get at the machine's srvtab entry, so we hang on to
566 * some root privledges for now. We give them up entirely later.
571 initgroups(CLIENT_LOGIN, client_gid);
574 #endif /* KRB4_SECURITY */
576 #endif /* FORCE_USERID */
578 fputs("Remote user: ", stdout);
580 if ((remoteuser = agets(stdin)) == NULL) {
583 str = stralloc2("USER ", remoteuser);
585 fputs("Remote host: ", stdout);
587 if ((remotehost = agets(stdin)) == NULL) {
591 set_pname("security");
594 if ((hp = gethostbyname(remotehost)) == NULL) {
595 dbprintf(("%s: cannot look up remote host %s\n",
596 debug_prefix_time(NULL), remotehost));
599 memcpy((char *)&fake.sin_addr, (char *)hp->h_addr, sizeof(hp->h_addr));
600 fake.sin_port = htons(IPPORT_RESERVED - 1);
602 if ((r = bsd_security_ok(&fake, str, 0, &errstr)) == 0) {
603 dbprintf(("%s: security check of %s@%s failed\n",
604 debug_prefix_time(NULL), remoteuser, remotehost));
605 dbprintf(("%s: %s\n", debug_prefix(NULL), errstr));
607 dbprintf(("%s: security check of %s@%s passed\n",
608 debug_prefix_time(NULL), remoteuser, remotehost));