/* * COPYRIGHT NOTICE * Copyright 1990, Silicon Graphics, Inc. All Rights Reserved. * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.; * the contents of this file may not be disclosed to third parties, copied or * duplicated in any form, in whole or in part, without the prior written * permission of Silicon Graphics, Inc. * * RESTRICTED RIGHTS LEGEND: * Use, duplication or disclosure by the Government is subject to restrictions * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data * and Computer Software clause at DFARS 252.227-7013, and/or in similar or * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - * rights reserved under the Copyright Laws of the United States. * */ #ident "$Revision: 1.9 $" /* * * chlabel - change file label */ #include #include #include #include #include #include #include #include #include #include #include #include #include static cap_value_t lstat_caps[] = { CAP_DAC_EXECUTE, CAP_NOT_A_CID, /* really: CAP_DAC_READ_SEARCH */ CAP_NOT_A_CID, /* really: CAP_MAC_READ */ }; static cap_value_t caps_for_mac_set_file[] = { CAP_NOT_A_CID, /* really: CAP_DAC_READ_SEARCH */ CAP_NOT_A_CID, /* really: CAP_MAC_READ */ CAP_DAC_EXECUTE, CAP_FOWNER, CAP_MAC_DOWNGRADE, CAP_MAC_UPGRADE, CAP_MAC_WRITE, CAP_DEVICE_MGT, CAP_DAC_WRITE, CAP_MAC_RELABEL_OPEN, }; static cap_value_t caps_for_mac_set_proc[] = { CAP_MAC_MLD, CAP_MAC_RELABEL_SUBJ, }; static void print_usage_and_die (char *program) { fprintf (stderr, "%s: usage:\n", program); fprintf (stderr, "\t%s [-f] -m pathname\n", program); fprintf (stderr, "\t%s [-f] label pathname ...\n", program); exit (EXIT_FAILURE); } static int cap_inheritable (cap_t pcap, cap_value_t cap) { cap_flag_value_t flag; return (cap_get_flag (pcap, cap, CAP_INHERITABLE, &flag) == 0 && flag == CAP_SET); } static int cap_permitted (cap_t pcap, cap_value_t cap) { cap_flag_value_t flag; return (cap_get_flag (pcap, cap, CAP_PERMITTED, &flag) == 0 && flag == CAP_SET); } static mac_t pmac; /* current process label */ static mac_t pmold; /* current process label, moldy */ static void free_pmac (void) { if (pmac); mac_free (pmac); if (pmold) mac_free (pmold); } int main (int argc, char *argv[]) { mac_t file_mac; /* existing file label */ mac_t new_mac; /* new file label */ cap_t ocap; /* saved process capabilities */ char *program; /* Program name saved */ int c; /* For use by getopt(3) */ int force = 0; /* relabel even if open */ int mflag = 0; /* set moldy bit */ int lstat_ncap = 1; int msf_ncap = 2; /* find program name */ program = strrchr (argv[0], '/'); program = (program != NULL ? program + 1 : argv[0]); /* only work if MAC is enabled */ if (sysconf (_SC_MAC) <= 0) { fprintf (stderr, "%s: MAC not enabled.\n", program); return (EXIT_FAILURE); } /* * get current process label and * create moldy version of current process label and * register label destroy function */ if ((pmac = mac_get_proc ()) == NULL || (pmold = mac_set_moldy (pmac)) == NULL || atexit (free_pmac)) { fprintf (stderr, "%s: fatal error at %s(%d).\n", program, __FILE__, __LINE__); return (EXIT_FAILURE); } /* only work if CAP is enabled */ if (sysconf (_SC_CAP) <= 0) { fprintf (stderr, "%s: capabilities not enabled.\n", program); return (EXIT_FAILURE); } /* * Verify that the required capabilities are available. * CAP_DAC_READ_SEARCH is required by sgi_getclearancebyname * CAP_MAC_READ is required by sgi_getclearancebyname * CAP_PRIV_PORT is required by sgi_getclearancebyname * * CAP_MAC_MLD is required by chlabel itself * CAP_MAC_RELABEL_SUBJ is required by chlabel itself * * If CAP_DAC_READ_SEARCH or CAP_MAC_READ were inherited, then * it's ok to acquire them. If not, then the * invoking user isn't cleared for these capabilities, * so we must not acquire them. */ if ((ocap = cap_get_proc ()) == NULL) { fprintf (stderr, "%s: fatal error at %s(%d).\n", program, __FILE__, __LINE__); return (EXIT_FAILURE); } if (!cap_permitted (ocap, CAP_DAC_READ_SEARCH) || !cap_permitted (ocap, CAP_MAC_READ) || !cap_permitted (ocap, CAP_PRIV_PORT) || !cap_permitted (ocap, CAP_MAC_MLD) || !cap_permitted (ocap, CAP_MAC_RELABEL_SUBJ) || !cap_permitted (ocap, CAP_DAC_WRITE)) { cap_free (ocap); fprintf (stderr, "%s: insufficient privilege\n", program); return (EXIT_FAILURE); } if (cap_inheritable (ocap, CAP_DAC_READ_SEARCH)) { lstat_caps[lstat_ncap++] = CAP_DAC_READ_SEARCH; caps_for_mac_set_file[--msf_ncap] = CAP_DAC_READ_SEARCH; } if (cap_inheritable (ocap, CAP_MAC_READ)) { lstat_caps[lstat_ncap++] = CAP_MAC_READ; caps_for_mac_set_file[--msf_ncap] = CAP_MAC_READ; } cap_free (ocap); /* parse option arguments */ while ((c = getopt (argc, argv, "fm")) != -1) { switch (c) { case 'f': force = 1; break; case 'm': mflag = 1; break; default: print_usage_and_die (program); break; } } /* * Unless the -m flag was set the first parameter is the label. */ if (!mflag) { struct clearance *clp; /* user clearance information */ struct passwd *pw; /* user name information */ if (optind >= argc) print_usage_and_die (program); /* convert human-readable label to internal form */ if ((new_mac = mac_from_text (argv[optind])) == NULL) { fprintf (stderr, "%s: invalid label: \"%s\"\n", program, argv[optind]); return (EXIT_FAILURE); } /* get /etc/passwd info */ if ((pw = getpwuid (getuid ())) == NULL) { fprintf (stderr, "%s cannot find user's name.\n", program); return (EXIT_FAILURE); } /* get the user's label clearance range */ if ((clp = sgi_getclearancebyname (pw->pw_name)) == NULL) { fprintf (stderr, "%s cannot find user %s's clearance.\n", program, pw->pw_name); return (EXIT_FAILURE); } /* check the label against the user's clearance range */ if ((c = mac_cleared_fl (clp, new_mac)) != MAC_CLEARED) { mac_free (new_mac); fprintf (stderr, "%s: denied because %s.\n", program, mac_clearance_error (c)); return (EXIT_FAILURE); } optind++; } /* * There needs to be at least one path. * If -m is given, there can be only one path. */ if (optind >= argc || (mflag && (optind + 1) != argc)) print_usage_and_die (program); /* * Do everything with a moldy label. The case this has trouble * with is: * chlabel A/B * where A is a moldy directory. The argument is that one * oughtn't be changing the label on a file in a moldy directory * without first moving it to where it belongs. */ for (; optind < argc; optind++) { struct stat plain_st; /* attribute of file, normal label */ struct stat moldy_st; /* attribute of file, moldy label */ c = 0; /* give ourselves a moldy label */ ocap = cap_acquire (2, caps_for_mac_set_proc); if (mac_set_proc (pmold) == -1) { cap_surrender (ocap); fprintf (stderr, "%s: fatal error at %s(%d).\n", program, __FILE__, __LINE__); return (EXIT_FAILURE); } cap_surrender (ocap); /* get moldy file attributes */ ocap = cap_acquire (lstat_ncap, lstat_caps); c += (lstat (argv[optind], &moldy_st) == -1) ? 1 : 0; cap_surrender (ocap); /* go back to our normal label */ ocap = cap_acquire (2, caps_for_mac_set_proc); if (mac_set_proc (pmac) == -1) { cap_surrender (ocap); fprintf (stderr, "%s: fatal error at %s(%d).\n", program, __FILE__, __LINE__); return (EXIT_FAILURE); } cap_surrender (ocap); /* get non-moldy file attributes */ ocap = cap_acquire (lstat_ncap, lstat_caps); c += (lstat (argv[optind], &plain_st) == -1) ? 1 : 0; file_mac = mac_get_file (argv[optind]); cap_surrender (ocap); /* go to next file on error */ if (file_mac == NULL || c) { perror (argv[optind]); mac_free (file_mac); continue; } /* * Verify that using a moldy label doesn't result * in changing the wrong file's label. In other words, * avoid changing the label of a file through a path * which does moldy redirection. */ if (plain_st.st_ino != moldy_st.st_ino || plain_st.st_dev != moldy_st.st_dev) { fprintf (stderr, "%s: %s, %s.\n", program, argv[optind], "Multilevel directory redirection confusion."); mac_free (file_mac); continue; } /* create moldy new_mac as appropriate */ if (mflag) new_mac = mac_set_moldy (file_mac); /* * Only directories can be moldy. */ if (!S_ISDIR (plain_st.st_mode) && mac_is_moldy (new_mac)) { fprintf (stderr, "%s: Only directories can be moldy.\n", argv[optind]); mac_free (file_mac); if (mflag) mac_free (new_mac); continue; } /* * Shortcut out if the old and new labels are the same. * Moldy and normal labels compare the same with * mac_equal(), so we must also check the label types * here e.g. it should be possible to set a * msenlow/minthigh file to msenmldlow/minthigh. */ if (new_mac->ml_msen_type == file_mac->ml_msen_type && new_mac->ml_mint_type == file_mac->ml_mint_type && mac_equal (new_mac, file_mac)) { mac_free (file_mac); if (mflag) mac_free (new_mac); continue; } /* * Do the deed, if at all possible. */ ocap = cap_acquire (9 - msf_ncap + (force ? 1 : 0), &caps_for_mac_set_file[msf_ncap]); if (mac_set_file (argv[optind], new_mac) == -1) perror (argv[optind]); cap_surrender (ocap); mac_free (file_mac); if (mflag) mac_free (new_mac); } if (!mflag) mac_free (new_mac); return (EXIT_SUCCESS); }