Quoting Andrew G. Morgan ([EMAIL PROTECTED]): > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Subrata, > > I have to apologize. I'm not very familiar with LTP tests and have been > putting this off until I had time to figure all this out... I've still > not figured out how to run this but, browsing over the source code, have > noted a number of things. Please see my comments inline. > > PS. What should I read to get up to speed on LTP? > > Thanks > > Andrew > > Subrata Modak wrote: > | Andrew, > | > | Your thoughts on this ;-) > | > | --Subrata > | > |> On Mon, 2008-02-04 at 21:53 -0600, Serge E. Hallyn wrote: > |>> Hi Andrew, > |>> > |>> The original verify_caps_exec.c test in the filecaps test was written > |>> before libcap had file capabilities support. Faced with implementing > |>> 64-bit support in that ugly mess in order to properly test your > |>> per-process securebits patch, it seemed wise to just switch to using > |>> libcap :) Does the following new version of the file look kosher > |>> to you? > |> Hi Andrew, > |> > |> Can you please provide your comments on this test case from Sergei? We > |> are looking forward, and, would be happy to see this inside LTP post > |> comments. > |> > |> Regards-- > |> Subrata > |> > |>> thanks, > |>> -serge > |>> > |>> > /******************************************************************************/ > |>> /* > */ > |>> /* Copyright (c) International Business Machines Corp., 2007, 2008 > */ > |>> /* > */ > |>> /* This program is free software; you can redistribute it and/or > modify */ > |>> /* it under the terms of the GNU General Public License as published > by */ > |>> /* the Free Software Foundation; either version 2 of the License, or > */ > |>> /* (at your option) any later version. > */ > |>> /* > */ > |>> /* This program is distributed in the hope that it will be useful, > */ > |>> /* but WITHOUT ANY WARRANTY; without even the implied warranty of > */ > |>> /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See > */ > |>> /* the GNU General Public License for more details. > */ > |>> /* > */ > |>> /* You should have received a copy of the GNU General Public License > */ > |>> /* along with this program; if not, write to the Free Software > */ > |>> /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA > 02111-1307 USA */ > |>> /* > */ > |>> > /******************************************************************************/ > |>> /* > |>> * File: verify_caps_exec.c > |>> * Author: Serge Hallyn > |>> * Purpose: perform several tests of file capabilities: > |>> * 1. try setting caps without CAP_SYS_ADMIN > |>> * 2. test proper calculation of pI', pE', and pP'. > |>> * Try setting valid caps, drop rights, and run the executable, > |>> * make sure we get the rights > |>> */ > |>> > |>> #define _GNU_SOURCE > |>> #include <stdio.h> > |>> #include <unistd.h> > |>> #include <endian.h> > |>> #include <byteswap.h> > |>> #include <sys/types.h> > |>> #include <sys/stat.h> > |>> #include <sys/wait.h> > |>> #include <errno.h> > |>> #include <fcntl.h> > |>> #include <sys/capability.h> > |>> #include <sys/prctl.h> > |>> #include <test.h> > |>> > |>> #define TSTPATH "./print_caps" > |>> char *TCID = "filecaps"; > |>> int TST_TOTAL=1; > |>> > |>> int errno; > |>> > |>> void usage(char *me) > |>> { > |>> tst_resm(TFAIL, "Usage: %s <0|1> [arg]\n", me); > |>> tst_resm(TINFO, " 0: set file caps without CAP_SYS_ADMIN\n"); > > Not sure what is significant about CAP_SYS_ADMIN.
Documentation error. Note that the test drops all caps. > > |>> tst_resm(TINFO, " 1: test that file caps are set correctly on > exec\n"); > |>> tst_exit(1); > |>> } > |>> > |>> #define DROP_PERMS 0 > |>> #define KEEP_PERMS 1 > |>> > |>> void print_my_caps() > |>> { > |>> cap_t cap = cap_get_proc(); > |>> tst_resm(TINFO, "\ncaps are %s\n", cap_to_text(cap, NULL)); > > cap_free()? Yup, to all instances of this comment. > |>> } > |>> > |>> int drop_root(int keep_perms) > |>> { > |>> int ret; > |>> > |>> if (keep_perms) > |>> prctl(PR_SET_KEEPCAPS, 1); > |>> ret = setresuid(1000, 1000, 1000); > |>> if (ret) { > |>> perror("setresuid"); > |>> tst_resm(TFAIL, "Error dropping root privs\n"); > |>> tst_exit(4); > |>> } > |>> if (keep_perms) { > |>> cap_t cap = cap_from_text("=eip"); > |>> cap_set_proc(cap); > cap_free()? > > |>> } > |>> > |>> return 1; > |>> } > |>> > |>> /* > |>> * TODO: find a better way to do this. Emulate libcap's > |>> * way, or just take it from linux/capability.h > |>> */ > |>> #ifndef __CAP_BITS > |>> #define __CAP_BITS 34 > |>> #endif > > I guess you might be able to use PR_GET_SECUREBITS to tell you what the > max supported capability of the running kernel is. Good idea. Or if Kaigai's patch goes in that coudl be used... > |>> > |>> int perms_test(void) > |>> { > |>> int ret; > |>> cap_t cap; > |>> > |>> drop_root(DROP_PERMS); > |>> cap = cap_from_text("all=eip"); > |>> if (!cap) { > |>> tst_resm(TFAIL, "could not get cap from text for perms test\n"); > |>> return 1; > |>> } > |>> ret = cap_set_file(TSTPATH, cap); > |>> if (ret) { > |>> tst_resm(TPASS, "could not set capabilities as non-root\n"); > |>> ret = 0; > |>> } else { > |>> tst_resm(TFAIL, "could set capabilities as non-root\n"); > |>> ret = 1; > |>> } > |>> > |>> cap_free(cap); > |>> return ret; > |>> } > |>> > |>> #define FIFOFILE "caps_fifo" > |>> void create_fifo(void) > |>> { > |>> int ret; > |>> > |>> ret = mkfifo(FIFOFILE, S_IRWXU | S_IRWXG | S_IRWXO); > |>> if (ret == -1 && errno != EEXIST) { > |>> perror("mkfifo"); > |>> tst_resm(TFAIL, "failed creating %s\n", FIFOFILE); > |>> tst_exit(1); > |>> } > |>> } > |>> > |>> void write_to_fifo(char *buf) > |>> { > |>> int fd; > |>> > |>> fd = open(FIFOFILE, O_WRONLY); > |>> write(fd, buf, strlen(buf)); > |>> close(fd); > |>> } > |>> > |>> void read_from_fifo(char *buf) > |>> { > |>> int fd; > |>> > |>> memset(buf, 0, 200); > |>> fd = open(FIFOFILE, O_RDONLY); > |>> if (fd < 0) { > |>> perror("open"); > |>> tst_resm(TFAIL, "Failed opening fifo\n"); > |>> tst_exit(1); > |>> } > |>> read(fd, buf, 199); > |>> close(fd); > |>> } > |>> > |>> int compare_caps(char *buf1, char *buf2) > |>> { > |>> int res; > |>> > |>> res = strcmp(buf1, buf2) == 0; > |>> return res; > |>> } > |>> > |>> int fork_drop_and_exec(int keepperms, char *capstxt) > |>> { > |>> int pid; > |>> int ret = 0; > |>> char buf[200], *p; > |>> static int seqno = 0; > |>> > |>> pid = fork(); > |>> if (pid < 0) { > |>> perror("fork"); > |>> tst_resm(TFAIL, "%s: failed fork\n", __FUNCTION__); > |>> tst_exit(1); > |>> } > |>> if (pid == 0) { > |>> drop_root(keepperms); > |>> print_my_caps(); > |>> sprintf(buf, "%d", seqno); > |>> ret = execlp(TSTPATH, TSTPATH, buf, NULL); > |>> perror("execl"); > |>> tst_resm(TFAIL, "%s: exec failed\n", __FUNCTION__); > |>> snprintf(buf, 200, "failed to run as %s\n", capstxt); > |>> write_to_fifo(buf); > |>> tst_exit(1); > |>> } else { > |>> p = buf; > |>> while (1) { > |>> int c, s; > |>> read_from_fifo(buf); > |>> c = sscanf(buf, "%d", &s); > |>> if (c==1 && s==seqno) > |>> break; > |>> tst_resm(TINFO, "got a bad seqno (c=%d, s=%d, > seqno=%d)", > |>> c, s, seqno); > |>> } > |>> p = index(buf, '.')+1; > |>> if (p==(char *)1) { > |>> tst_resm(TFAIL, "got a bad message from print_caps\n"); > |>> tst_exit(1); > |>> } > |>> tst_resm(TINFO, "Expected to run as .%s., ran as .%s..\n", > |>> capstxt, p); > |>> if (strcmp(p, capstxt) != 0) { > |>> tst_resm(TINFO, "those are not the same\n"); > |>> ret = -1; > |>> } > |>> seqno++; > |>> } > |>> return ret; > |>> } > |>> > |>> int caps_actually_set_test(void) > |>> { > |>> int whichset, whichcap, finalret = 0, ret; > |>> cap_t cap, pcap; > |>> char *capstxt; > > captext gets allocated in multiple places, but I can't see where it is > cap_free()'d. Good point. Though note that the cap_to_text manpage is confusing here - do i free(capstxt), or do I cap_free(cap) as the manpage implies and automatically free capstxt that way? Reading the manpage on my system kind of implies that so long as I cap_init at the start and cap_free(cap) at the end, capstxt will be freed at the end... Though that's probably not right. > |>> cap_value_t capvalue[1]; > |>> > |>> cap = cap_init(); > |>> pcap = cap_init(); > |>> if (!cap || !pcap) { > |>> perror("cap_init"); > |>> exit(2); > |>> } > |>> > |>> create_fifo(); > |>> > |>> /* first, try each bit in fP (forced) with fE on and off. */ > |>> for (whichcap=0; whichcap < __CAP_BITS; whichcap++) { > |>> /* fE = 0, don't gain the perm */ > |>> capvalue[0] = whichcap; > |>> cap_clear(cap); > |>> cap_set_flag(cap, CAP_PERMITTED, 1, capvalue, CAP_SET); > |>> ret = cap_set_file(TSTPATH, cap); > |>> if (ret) { > |>> tst_resm(TINFO, "%d %d\n", whichset, whichcap); > > Not clear where whichset is initialized. In the previous version :) > If TSTPATH refers to a file that it not present, then this test seems to > pass. Is that intended? No, just an assumption of proper ltp setup. Should fix that. > |>> continue; > |>> } > |>> capstxt = cap_to_text(cap, NULL); > |>> ret = fork_drop_and_exec(DROP_PERMS, capstxt); > |>> if (ret) { > |>> tst_resm(TINFO, "Failed CAP_PERMITTED=%d > CAP_EFFECTIVE=0\n", > |>> whichcap); > |>> if (!finalret) > |>> finalret = ret; > |>> } > |>> > |>> /* SERGE here */ > |>> /* fE = 1, do gain the perm */ > |>> cap_clear(cap); > |>> cap_set_flag(cap, CAP_PERMITTED, 1, capvalue, CAP_SET); > |>> cap_set_flag(cap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); > |>> ret = cap_set_file(TSTPATH, cap); > |>> if (ret) { > |>> tst_resm(TINFO, "%d %d\n", whichset, whichcap); > |>> continue; > |>> } > |>> capstxt = cap_to_text(cap, NULL); > |>> if (strcmp(capstxt, "=")==0) { > |>> tst_resm(TINFO, "%s: libcap doesn't know about cap %d, > not > running\n", > |>> __FUNCTION__, whichcap); > |>> ret = 0; > |>> } else > |>> ret = fork_drop_and_exec(DROP_PERMS, capstxt); > |>> if (ret) { > |>> tst_resm(TINFO, "Failed CAP_PERMITTED=%d > CAP_EFFECTIVE=1\n", > |>> whichcap); > |>> if (!finalret) > |>> finalret = ret; > |>> } > |>> } > |>> > |>> > |>> /* > |>> * next try each bit in fI > |>> * The first two attemps have the bit which is in fI in pI. > |>> * This should result in the bit being in pP'. > |>> * If fE was set then it should also be in pE'. > |>> * The last attempt starts with an empty pI. > |>> * This should result in empty capability, as there were > |>> * no bits to be inherited from the original process. > |>> */ > |>> for (whichcap=0; whichcap < __CAP_BITS; whichcap++) { > |>> int i; > |>> /* > |>> * bit is in fI and pI, so should be in pI'. > |>> * but fE=0, so cap is in pP' but not pE'. > |>> */ > |>> cap_clear(cap); > |>> cap_clear(pcap); > |>> for (i=0; i<__CAP_BITS; i++) { > |>> capvalue[0] = i; > |>> cap_set_flag(pcap, CAP_INHERITABLE, 1, capvalue, > CAP_SET); > |>> } > |>> capvalue[0] = whichcap; > |>> cap_set_flag(cap, CAP_INHERITABLE, 1, capvalue, CAP_SET); > |>> ret = cap_set_file(TSTPATH, cap); > |>> if (ret) { > |>> tst_resm(TINFO, "%d %d\n", whichset, whichcap); > |>> continue; > |>> } > |>> cap_set_flag(pcap, CAP_PERMITTED, 1, capvalue, CAP_SET); > |>> capstxt = cap_to_text(pcap, NULL); > |>> ret = fork_drop_and_exec(KEEP_PERMS, capstxt); > |>> if (ret) { > |>> tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d " > |>> "CAP_EFFECTIVE=0\n", whichcap); > |>> if (!finalret) > |>> finalret = ret; > |>> } > |>> > |>> /* > |>> * bit is in fI and pI, so should be in pI'. > |>> * and fE=1, so cap is in pP' and pE'. > |>> */ > |>> > |>> cap_set_flag(cap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); > |>> ret = cap_set_file(TSTPATH, cap); > |>> if (ret) { > |>> tst_resm(TINFO, "%d %d\n", whichset, whichcap); > |>> continue; > |>> } > |>> cap_set_flag(pcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); > |>> capstxt = cap_to_text(pcap, NULL); > |>> if (strcmp(capstxt, "=")==0) { > |>> tst_resm(TINFO, "%s: libcap doesn't know about cap %d, > not > running\n", > |>> __FUNCTION__, whichcap); > |>> ret = 0; > |>> } else > |>> ret = fork_drop_and_exec(KEEP_PERMS, capstxt); > |>> if (ret) { > |>> tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d " > |>> "CAP_EFFECTIVE=1\n", whichcap); > |>> if (!finalret) > |>> finalret = ret; > |>> } > |>> > |>> /* > |>> * bit is in fI but not in pI > |>> * So pP' is empty. > |>> * pE' must be empty. > |>> */ > |>> cap_clear(cap); > |>> capstxt = cap_to_text(cap, NULL); > |>> ret = fork_drop_and_exec(DROP_PERMS, capstxt); > |>> if (ret) { > |>> tst_resm(TINFO, "Failed without_perms > CAP_INHERITABLE=%d", > |>> whichcap); > |>> if (!finalret) > |>> finalret = ret; > |>> } > |>> } > |>> > |>> cap_free(cap); > |>> return finalret; > |>> } > |>> > |>> int main(int argc, char *argv[]) > |>> { > |>> int ret = 0; > |>> > |>> if (argc < 2) > |>> usage(argv[0]); > |>> > |>> switch(atoi(argv[1])) { > |>> case 0: > |>> ret = perms_test(); > |>> break; > |>> case 1: > |>> ret = caps_actually_set_test(); > |>> if (ret) > |>> tst_resm(TFAIL, "Some tests failed\n"); > |>> else > |>> tst_resm(TPASS, "All tests passed\n"); > |>> break; > |>> default: usage(argv[0]); > |>> } > |>> > |>> tst_exit(ret); > |>> } Thanks Andrew, I'll try to get around to sending a new patch "soon". In the meantime, i would argue that applying the existing patch is better than doing nothing since the previous version does not work with 64-bit capabilities. Is that ok with you? thanks, -serge ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list