Saturday, November 21, 2009

Selection of standards














2.3 Selection of standards


Standards must be selected that apply to a company's specific needs, environment, and resources.


Standards for almost everything are available, often from several different sources. As described in Section 2.2, standards can be obtained from industry and professional groups, other companies, private consultants, or the government. They can also be prepared in-house. The major concern, then, is not where to find them, but to be sure that the ones being obtained are necessary and proper for the given organization. Even in a particular company, all the standards in use in one data center may not apply to a second data center. For example, the company's scientific data processing needs may call for different standards from its financial data processing center.


Many things can affect the need for standards and the standards needed. As stated, different data processing orientations may call for specific standards. Such things as runtimes, terminal response times, language selection, operating systems, and telecommunications protocols are all subject to standardization on different terms in different processing environments. Even such things as programmer workstation size and arrangement, data input and results output locations, training and educational needs, and data access rights often are determined on a basis that includes the type of processing as a consideration.


Not all standards available for a given subject or topic may apply in every situation. Language standards, particularly selecting the languages to be used, may have exceptions or not be applied at all. A particular standard life-cycle model may be inappropriate in a given instance or for a specific project. Data access or telecommunications standards may be modified or waived to fit a particular project or installation.















Section 2.6. From the Java Library: java.util.Scanner










[Page 91 (continued)]

2.6. From the Java Library: java.util.Scanner


IF WE WISH to write useful interactive programs, we must be able to receive information from users as well as send information to them. We saw in the preceding chapter that output from a program can be sent to the console window by simply using the System.out.print() and System.out.println() statements. In this section we describe two simple ways that Java can handle keyboard input. Receiving input from the keyboard, together with sending output to the console window, creates one of the standard user interfaces for programs.


java.sun.com/docs



Recall that in Java, any source or destination for I/O is considered a stream of bytes or characters. To perform keyboard input, we will extract characters from System.in, the input stream connected to the keyboard. Getting keyboard input from System.in involves two complications that are not present in dealing with System.out.println(). First, the normal keyboard input data requested of a user consists of a sequence of characters or digits that represent a word, phrase, integer, or real number. Normally, an entire sequence of characters typed by the user will represent data to be stored in a single variable, with the user hitting the return or enter key to signal the end of a piece of requested data. Java has a special class, Buffered Reader, that uses an input stream and has a method that collects characters until it reads the character or characters that correspond to hitting the return or enter key. A second complication for reading input involves the problem of how to handle receiving data that is not in the same format as expected. The BufferedReader class handles this problem by using certain exceptions, a special kind of error message that must be handled by the programmer. Chapter 10 is devoted to exceptions, and we will avoid their use, as far as possible, until that time.


There is an alternative way to handle keyboard input in the Java 2 Platform Standard Edition 5.0 (J2SE 5.0). A Scanner class has been added to the java.util package which permits keyboard input without forcing the programmer to handle exceptions. We introduce the Scanner class in the next subsection and then describe how a user-defined class introduced in Chapter 4 can function in an equivalent fashion to permit simple keyboard input.



2.6.1. Keyboard Input with the Scanner Class


A partial definition of Scanner is shown in Figure 2.25. Note that the Scanner methods listed are but a small subset of the public methods of this class. The Scanner class is in the java.util package, so classes that use it should import it with the following statement:


import java.util.Scanner;




[Page 92]



Figure 2.25. The Scanner class, with a partial list of its public methods.







The Scanner class is designed to be a very flexible way to recognize chunks of data that fit specified patterns from any input stream. To use the Scanner class for keyboard input, we must create a Scanner instance and associate it with System.in. The class has a constructor for this purpose, so the statement


Scanner sc = new Scanner(System.in);


declares and instantiates an object that can be used for keyboard input. After we create a Scanner object, we can make a call to nextInt(), nextdouble(), or next() to read, respectively, an integer, real number, or string from the keyboard. The program in Figure 2.26 demonstrates how an integer would be read and used. When the nextInt() method is executed, no further statements are executed until an int value is returned by the method. Normally this does not happen until the user has typed in the digits of an integer and hit the return or enter key. Thus, executing the main() method of the TestScanner class will result in the output


Input an integer:


Figure 2.26. A very brief program with a Scanner object used for keyboard input.





import java.util.Scanner;

public class TestScanner
{
public static void main(String[] args)
{ // Create Scanner object
Scanner sc = new Scanner(System.in);
System.out.print("Input an integer:"); // Prompt
int num = sc.nextInt(); // Read an integer
System.out.println(num + " squared = " + num*num);
} // main()
} // TestScanner class




to the console window, and the program will wait for the user to type in an integer and hit the return or enter key. After this has been done the output will look something like:


Input an integer:123
123 squared = 15129



[Page 93]

Keyboard input of real numbers and strings is handled in a similar manner.


Keyboard input will allow us to create examples of command-line interfaces for interactive programs. For example, the code


Scanner sc = new Scanner(System.in);
Riddle riddle = new Riddle(
"What is black and white and red all over?",
"An embarrassed zebra.");
System.out.println("Here is a riddle:");
System.out.println(riddle.getQuestion());
System.out.print("To see the answer, "); // Prompt
System.out.println("type a letter and enter.");
String str = sc.next(); // Wait for input
System.out.println(riddle.getAnswer());


will display a riddle question and prompt the user to type a letter and hit the enter key to see the answer. In the next chapter, we will develop new methods for the OneRowNim class that will be able to use int values input from the keyboard for the next move.


Since the Scanner class is designed as a flexible tool for recognizing chunks of data from any input stream, it has some properties that may be unexpected and not totally compatible with simple keyboard input. A Scanner object has a set of character strings that separate, or delimit, the chunks of data it is looking for. By default, this set of delimiters consists of any nonempty sequence of white space characters, that is, the space, tab, return, and newline characters. This will allow a user to input several integers separated by spaces before hitting the enter key. This might be handled by code like:


System.out.print("Input two integers and an enter:");
int num1 = sc.nextInt();
int num2 = sc.nextInt();


White space as delimiters also means that the next() method cannot return an empty string, nor can it return a string that contains any spaces. For example, consider the code:


System.out.print("Input the first president of the USA:");
String str = sc.next();


If one types "George Washington" and hits the enter key, the string str will store only "George." In order to get a Scanner object to read strings that contain spaces, we must use the useDelimiter() method to define the set of delimiters as just that character string generated by hitting the enter key. For example, for some Windows operating systems, the statement


sc = sc.useDelimiter("\r\n");


will result in the next() method returning the entire string of characters input from the keyboard, up to but not including those generated by hitting the enter key.



[Page 94]

The fact that we can use a Scanner object to write Java code that ignores exceptions does not mean that exceptions will not be generated by keyboard input. If the user enters letters rather than digits for the nextInt() method to process, the program will be terminated with an error message.


It must be stressed that the strategy for handling keyboard input outlined above is a temporary strategy until the topic of exceptions is covered in Chapter 11. Real software applications that use keyboard input should carefully handle the possibility that a user will enter something unexpected. In Java, this can only be done by handling exceptions.




2.6.2. Keyboard Input with the KeyboardReader Class


If you are using an older version of Java that does not have the Scanner class, a user-defined class can be used instead. A KeyboardReader class that uses the BufferedReader class will be developed in Chapter 4. It has methods that read data from the keyboard in a manner very similar to those of the Scanner class. A partial list of its public methods is given in the UML class diagram shown in Figure 2.27. To use the KeyboardReader class for keyboard input, copy the source code KeyboardReader.java from Chapter 4 into the same directory as the source code of your current Java class (and add it to your current project if you are using a integrated development environment).




Figure 2.27. A UML class diagram of the KeyboardReader class.







To use a KeyboardReader object, we need to create an instance of the class with a constructor. Then calling one of the three methods will return an int, double, or String when data is input from the keyboard. Any of the three methods of a KeyboardReader object will attempt to process the entire string input from the keyboard up to the point that the enter key is hit. That is, the character or characters generated by hitting the return or enter key are the delimiters used by KeyboardReader. The TestKeyboardReader class definition in Figure 2.28 reads an integer from the keyboard and squares it just like the TestScanner class. In the remainder of the text, any time the Scanner class is used for keyboard input, the same program can be run using the KeyboardReader class after making the obvious substitutions.


Figure 2.28. A very brief program with a KeyboardReader object used for keyboard input.





public class TestKeyboardReader
{
public static void main(String[] args)
{ // Create KeyboardReader object
KeyboardReader kb = new KeyboardReader();
System.out.print("Input an integer:"); // Prompt
int num = kb.getKeyboardInteger(); // Read an integer
System.out.println(num + " squared = " + num*num);
} // main()
} // TestKeyboardReader class







[Page 95]
Self-Study Exercise

Exercise 2.8

Modify the main() method of the TestScanner class so that it reads a real number from the keyboard rather than an integer.














Bibliography











Bibliography


Berners-Lee, T., Fielding, R., and Frystyk, H. "Request for Comments (RFC) 1945: Hypertext Transfer Protocol HTTP/1.0." Internet Engineering Task Force (IETF), 1996.


Bishop, M. Computer Security: Art & Science. Addison-Wesley, 2003.


Brown, K. Programming Windows Security. Addison-Wesley, 2000.


Brown, K. The .NET Developer's Guide to Windows Security. Addison-Wesley, 2005.


Chen, H., Wagner, D., and Dean, D. "Setuid Demystified." In Proceedings of the Eleventh Usenix Security Symposium. San Francisco, 2002.


Eddon, G. and Eddon, H. Inside Distributed COM. Microsoft Press, 1998.


Ferguson, N. and Schneier, B. Practical Cryptography. Wiley Publishing, Inc., 2003.


Fielding, R., et al. (1999). "Request for Comments (RFC) 2616: Hypertext Transfer Protocol HTTP/1.1." Internet Engineering Task Force (IETF), 1999.


Hart, J. Windows System Programming, Third Edition. Addison-Wesley, 2005.


Hoglund, G. and McGraw, G. Exploiting Software. Addison-Wesley Professional, 2004.


Howard, M. and LeBlanc, D. Writing Secure Code, Second Edition. Microsoft Press, 2002.


Howard, M., LeBlanc, D., and Viega, J. 19 Deadly Sins of Software Security. McGraw-Hill Osborne Media, 2005.


ISO/IEC. ISO/IEC International Standard 9899-1999: Programming LanguagesC. International Organization for Standardization (ISO), 1999.


ITU-T. Recommendation X.690, ISO/IEC 8825-1, ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) & Distinguished Encoding Rules (DER). International Organization for Standardization (ISO), 2002.


ITU-T. Recommendation X.691, ISO/IEC 8825-2, ASN.1 encoding rules: Specification of Packed Encoding Rules (PER). International Organization for Standardization (ISO), 2003.


ITU-T. Recommendation X.693, ISO/IEC 8825-4, ASN.1 encoding rules: XML Encoding Rules (XER). International Organization for Standardization (ISO), 2004.


Kernighan, B. W. and Ritchie, D. M. The C Programming Language, 2nd Edition. Prentice Hall, 1988.


Koziol, J., et al. The Shellcoder's Handbook: Discovering & Exploiting Security Holes. Wiley Publishing, Inc., 2004.


Lopatic, T., McDonald, J., and Song, D. A Stateful Inspection of FireWall-1. Blackhat Briefings, 2000.


Maughan, D., et al. "Request for Comments (RFC) 2408: Internet Security Association & Key Management Protocol (ISAKMP)." Internet Engineering Task Force (IETF), 1998.


McConnell, S. Code Complete: A Practical Handbook of Software Construction. Microsoft Press, 2004.


Menezes, A., van Oorschot, P., and Vanstone, S. Handbook of Applied Cryptography. CRC Press, 2000.


Microsoft Developer Network (MSDN) Library. http://msdn.microsoft.com/library/, 2006.


Mockapetris, P. "Request for Comments (RFC) 1035: Domain NamesImplementation & Specification." Internet Engineering Task Force (IETF), 1987.


Moore, B. "Shattering By Example." Security-Assessment.com (http://blackhat.com/presentations/bh-usa-04/bh-us-04-moore/bh-us-04-moore-whitepaper.pdf), 2003.


NGSSoftware Insight Security Research Papers. Next Generation Security Software. http://www.nextgenss.com/research/papers/.


OpenBSD Project. OpenBSD Manual (www.openbsd.org/cgi-bin/man.cgi), 2006.


Paxon, V. Personal Web site (www.icir.org/vern/).


Postel, J. "Request for Comments (RFC) 0768: User Datagram Protocol." Internet Engineering Task Force (IETF), 1980.


Postel, J. "Request for Comments (RFC) 0791: Internet Protocol." Internet Engineering Task Force (IETF), 1981.


Postel, J. "Request for Comments (RFC) 0793: Transmission Control Protocol." Internet Engineering Task Force (IETF), 1981.


Quinlan, D., Russell, P. R., and Yeoh, C. "Filesystem Hierarchy Standard." www.pathname.com/fhs/, 2004.


Ranum, M. Personal Web site (www.ranum.com/).


Russinovich, M. and Cogswell, B. Sysinternals (www.sysinternals.com/).


Russinovich, M. and Solomon, D. Microsoft Windows Internals: Microsoft Windows Server 2003, Windows XP, & Windows 2000, Fourth Edition. Microsoft Press, 2005.


Schneier, B. Applied Cryptography: Protocols, Algorithms, & Source Code in C, Second Edition. Wiley Publishing, Inc., 1995.


Schrieber, S. Undocumented Windows 2000 Secrets: A Programmer's Cookbook. Addison-Wesley, 2001.


Sommerville, I. Software Engineering, Seventh Edition. Addison-Wesley, 2004.


SPI Labs Whitepapers. SPI Dynamics (www.spidynamics.com/spilabs/education/whitepapers.html).


St. Johns, M. "Request for Comments (RFC) 1413: Identification Protocol." Internet Engineering Task Force (IETF), 1993.


Stevens, W. R. Advanced Programming in the UNIX™ Environment. Addison-Wesley, 1992.


Stevens, W. R. TCP/IP Illustrated, Volume 1: The Protocols. Addison-Wesley, 1994.


Stewart, R. and Dalal, M. Improving TCP's Robustness to Blind In-Window Attacks. Internet Engineering Task Force (IETF), 2006.


Swiderski, F. and Snyder, W. Threat Modeling. Microsoft Press, 2004.


The Open Group. The Single UNIX Specification. The Austin Group (www.unix.org/version3/), 2004.


van der Linden, P. Expert C Programming. Prentice-Hall, 1994.


Wheeler, D. A. "Secure Programming for Linux and Unix HOWTO." www.dwheeler.com/secure-programs, 2003.


Zalewski, M. "Delivering Signals for Fun & Profit." Symantec (BindView publication, acquired by Symantec; www.bindview.com/Services/Razor/Papers/2001/signals.cfm), 2001.


Zalewski, M. Personal Web site (http://lcamtuf.coredump.cx/).












6.6 Community Enforcement of Open Source and Free Software Licenses











 < Day Day Up > 







6.6 Community Enforcement of Open Source and Free Software Licenses





The open source and free software communities

are also critical to the practical enforcement of open source and

free software licenses. While the discussion so far has focused on

the legal and practical reasons why open source and free software

licenses tend to be complied with, there is a more fundamental reason

why most programmers comply with such licenses. Non-compliance, or at

least knowing non-compliance with the terms of these licenses, is

simply wrong.





The world of open source and free software licensing is still a

relatively small one. As has already been described in previous

chapters, the code written under these licenses is mostly the work of

volunteers who have dedicated huge amounts of time, and, in many

cases, significant parts of their lives to the development and

distribution of good code for the benefit of as many people as

possible. In the course of writing this code and supporting these

projects, these programmers have foregone significantly more

lucrative opportunities offered by commercial software companies.

Behind the black and white terms and restrictions of these licenses,

which have taken up the bulk of this book, is a real principle. Free

code, however free may be defined, is a social good in itself. This

is the goal that is being pursued. However that goal may be reached,

whatever avenue of development is followed, this principle is held

above all others.





This principle is deeply felt by this community. The gross violation

of it by taking someone else's work and distributing

it as one's own is unthinkable. This moral principle

is, by itself, responsible for the largest part for the enforcement

of open source and free software licenses, not the texts of the

licenses themselves, and not the courts that enforce those

licenses.[6]

[6] For more discussion of this principle, see

the essay Homesteading the Noosphere

in The Cathedral & The Bazaar: Musings on

Linux and Open Source by an Accidental Revolutionary
, Eric

S. Raymond (O'Reilly 2001) (rev. ed.), and the

chapter The Art of Code in rebel code:

inside linux and the open source revolution
, Glyn Moody

(Perseus Publishing 2001).





Even those who have not internalized this principle have good reason

to abide by the norms of this community. Violating those norms will

incur, at the least, the displeasure of this community. Given the

number of people in this community and, perhaps more importantly, the

knowledge and capabilities of its members, such a violation can

result in the ostracism of the violator. Such a person might find his

emails remaining unanswered, being ignored or flamed in usegroups,

and being excluded from projects, whether under the open source or

free software banner, that involve members of this

community.[7]

[7] Those interested in the enforcement of

social norms that parallel legal restrictions should read

Order Without Law: How Neighbors Settle

Disputes
, Robert C. Ellickson (Harvard, 1991). While this

book addresses primarily the enforcement of social norms among cattle

ranchers in Shasta County, California, its analysis is no less

applicable to "virtual" communities

such as the open source and free software communities.





This does not mean there is a univerally shared view as to the

purpose of open source and free software licensing or the best way to

realize that purpose. As noted earlier, there are real ideological

differences between, for example, the "open

source" community and the "free

software" community. That said, there is

considerable common ground. One principle, which is universally

accepted, is that taking someone else's work and

modifying or distributing it in disregard of the intent of its

creator is wrong.





This should not be confused with the

"cross-over" of programmers (and

their code) from an open source project to a proprietarily licensed

projects. As described at the end of Chapter 2,

prominent open source programmers such as Bill

Joy and Eric

Allman moved

from open source to proprietary projects. In

Allman's case, he maintained both open source and

proprietary distributions of his popular Sendmail program in a way

consistent with both the terms and the principles of the original

license. Such movement does not (and should not) result in any ill

feeling against such individuals.





In sum, while contracts and courts are fundamental to protecting the

principles of open source and free software licensing, the real

guardians of these principles are programmers (and users) themselves.

















     < Day Day Up > 



    11.5 Summary



    [ Team LiB ]









    11.5 Summary


    The ATAM is a robust method for evaluating software architectures. It works by having project decision makers and stakeholders articulate a precise list of quality attribute requirements (in the form of scenarios) and by illuminating the architectural decisions relevant to carrying out each high-priority scenario. The decisions can then be cast as risks or nonrisks to find any trouble spots in the architecture.


    In addition to understanding what the ATAM is, it is also important to understand what it is not.



    • The ATAM is not an evaluation of requirements. That is, an ATAM-based evaluation will not tell anyone whether all of the requirements for a system will be met. It will discern whether gross requirements are satisfiable given the current design.


    • The ATAM is not a code evaluation. Because it is designed for use early in the life cycle, it makes no assumptions about the existence of code and has no provision for code inspection.


    • The ATAM does not include actual system testing. Again because the ATAM is designed for use early in the life cycle, it makes no assumptions of the existence of a system and has no provisions for any type of actual testing.


    • The ATAM is not a precise instrument, but identifies possible areas of risk within the architecture. These risks are embodied in the sensitivity points and the tradeoffs. The ATAM relies on the knowledge of the architect, and so it is possible that some risks will remain undetected. In addition, risks that are detected are not quantified. That is, there is no attempt to say that a particular sensitivity point will have a particular dollar value if not corrected. This final point will be addressed in Chapter 12 when we discuss the Cost Benefit Analysis Method (CBAM).



    We have participated in a large number of evaluations using the ATAM and taught and observed others performing them. In virtually every case, the reaction among the technical people being evaluated is amazement that so many risks can be found in such a short time. The reaction among management is that now they can understand why a particular technical issue threatens the achievement of their business goals. The ATAM has proven itself as a useful tool.







      [ Team LiB ]



      5.7 Types of Integers




      I l@ve RuBoard










      5.7 Types of Integers




      C++
      is considered a medium-level language because it allows you to get
      very close to the actual hardware of the machine. Some languages,
      such as Perl, go to great lengths to completely isolate the user from
      the details of how the processor works. This consistency comes at a
      great loss of efficiency. C++ lets you give detailed information
      about how the hardware is to be used.



      For example, most machines let you use different-length numbers. Perl
      allows you to use only one simple data type (the string[1]). This
      simplifies programming, but Perl programs are inefficient. C++ allows
      you to specify many different kinds of integers so you can make best
      use of the hardware.


      [1] Perl does have numbers such as 5, 8.3, and 20.8, but they are
      identical to the strings "5",
      "8.3", and
      "20.8".



      The type specifier int
      tells C++
      to use the most efficient size (for the machine you are using) for
      the integer. This can be 2 to 4 bytes depending on the machine.
      Sometimes you need extra digits to store numbers larger than what is
      allowed in a normal int.

      The declaration:



      long int answer;        // the answer of our calculations


      is used to allocate a long integer. The long qualifier informs C++ that you wish to
      allocate extra storage for the integer. If you are going to use small
      numbers and wish to reduce storage, use the qualifier

      short.



      short int year;         // Year including the century 


      C++ guarantees that the storage for short <= int <= long. In actual practice, short almost always allocates 2 bytes;
      long, 4 bytes; and int, 2 or 4 bytes. (See Appendix B for numeric ranges.)



      Long integer constants end with the
      character "L". For example:



      long int var = 1234L;    // Set up a long variable


      Actually you can use either an uppercase or a lowercase
      "L". Uppercase is preferred since
      lowercase easily gets confused with the digit
      "1".



      long int funny = 12l;   // Is this 12<long> or one hundred twenty-one?


      The type short int usually uses 2
      bytes, or 16 bits. Normally, 15 bits are used for the number and 1
      bit for the sign. This results in a range of -32,768
      (-215) to 32,767 (215 - 1).


      An unsigned
      short int
      uses all 16 bits for the number, giving it a
      range of 0 to 65,535 (216 - 1). All
      int declarations default to
      signed, so the declaration:



          signed long int answer;     // final result


      is the same as:



          long int answer;            // final result




      Finally
      there is the very short integer, the type char. Character variables are usually 1 byte
      long. They can also be used for numbers in the range of -128 to 127
      or 0 to 255. Unlike integers, they do not default to signed; the default is
      compiler-dependent.[2]


      [2] Borland-C++ even has a
      command-line switch to make the default for type char either signed or unsigned.



      Question: Is the following character variable signed or unsigned?



      char foo;


      Answers:




      1. It's signed.


      2. It's unsigned.


      3. It's compiler-dependent.


      4. If you always specify signed or
        unsigned, you don't
        have to worry about problems like this.




      Reading and writing very short integers is a little tricky. If you
      try to use a char variable in an
      output statement, it will be written�as a
      character
      . You need to trick C++ into believing that the
      char variable is an integer. This can be
      accomplished with the static_cast operator. Example 5-12 shows how to write a very short integer as a
      number.




      Example 5-12. two2/twoc.cpp

      #include <iostream>

      signed char ch; // Very short integer
      // Range is -128 to 127

      int main( )
      {
      std::cout << "The number is " << static_cast<int>(ch) << '\n';
      return (0);
      }



      We start by declaring a character variable ch.
      This variable is assigned the value 37. This is actually an integer,
      not a character, but C++ doesn't care. On the next
      line, we write out the value of the variable. If we tried to write
      ch directly, C++ would treat it as a character.
      The code static_cast<int>(ch) tells C++,
      "Treat this character as an
      integer."



      Reading a very short integer is not possible. You must first read in
      the number as a short int and then
      assign it to a very short integer variable.




      5.7.1 Summary of Integer Types



      long
      int


      declarations allow the programmer to
      explicitly specify extra precision where it is needed (at the expense
      of memory).
      short
      int
      numbers save space but have a more limited range. The
      most compact integers have type char.
      They also have the most limited range.



      unsigned
      numbers
      provide a way of doubling the range at the expense of eliminating
      negative numbers. The kind of number you use will depend on your
      program and storage requirements. The ranges of the various types of
      integers are listed in Appendix B.










        I l@ve RuBoard



        Traceability










         < Free Open Study > 





        Traceability



        An SRS is traceable if the origin of each of its requirements is clear and if it facilitates the referencing of each requirement in future development or enhancement documentation.



        The following two types of traceability are recommended:



        1. Backward traceability (i.e., to previous stages of development). This depends upon each requirement explicitly referencing its source in earlier documents.

        2. Forward traceability (i.e., to all documents spawned by the SRS). This depends upon each requirement in the SRS having a unique name or reference number.



        Forward traceability of the SRS is especially important when the software product enters the operation and maintenance phase. As code and design documents are modified, it is essential to be able to ascertain the complete set of requirements that may be affected by those modifications.












           < Free Open Study > 



          Privilege Vulnerabilities










          Privilege Vulnerabilities


          Now that you are familiar with the basic privilege management API, you can explore the types of mistakes developers are likely to make when attempting to perform privilege management.



          Reckless Use of Privileges


          The most straightforward type of privilege vulnerability happens when a program running with elevated privileges performs a potentially dangerous action on behalf of an unprivileged user without first imposing any limitations on itself with privilege management functions. Although it is possible for programs to safely access resources without needing to temporarily or permanently drop privileges, it is very easy to make mistakes when doing so.


          Here is a simple real-world example of a setuid root program named XF86_SVGA that used to ship with the XFree86 windowing package. Nicolas Dubee, a notorious and gifted researcher, discovered this vulnerability in 1997. Listing 9-1 is an excerpt from his advisory (available at http://packetstormsecurity.org/advisories/plaguez/plaguez.advisory.010.xfree86).


          Listing 9-1. Privilege Misuse in XFree86 SVGA Server





          [plaguez@plaguez plaguez]$ ls -al /etc/shadow
          -rw---- 1 root bin 1039 Aug 21 20:12 /etc/shadow
          [plaguez@plaguez bin]$ ID
          uid=502(plaguez) gid=500(users) groups=500(users)
          [plaguez@plaguez plaguez]$ cd /usr/X11R6/bin
          [plaguez@plaguez bin]$ ./XF86_SVGA -config /etc/shadow
          Unrecognized option: root:qEXaUxSeQ45ls:10171:-1:-1:-1:-1:-1:-1
          use: X [:<display>] [option]
          -a # mouse acceleration (pixels)
          -ac disable access control restrictions
          -audit int set audit trail level
          -auth file select authorization file
          bc enable bug compatibility
          -bs disable any backing store support
          -c turns off key-click




          The XF86_SVGA server, which was a setuid root program, happily read the configuration file /etc/shadow, and then proceeded to complain about the unrecognized option of root's password hash! The problem is that the X server would read in any configuration file the user requested as root, without regard for the actual user's permissions. Its configuration file parser happened to display a verbose error message, which printed the first line of the suspect configuration file.


          Considering the effects of any elevated group privileges is important, too. Many programs are installed as setgid so that they run as a member of a particular group. If the program performs a privileged action without relinquishing group privileges, it can still be vulnerable to a privilege escalation attack by allowing the user to access resources designated to the group in question.


          For example, the /sbin/dump program in NetBSD was installed as setgid tty so that it could notify system administrators if backup media needed to be changed. The dump program never dropped this group privilege, and local users could have the dump program start a program of their choice by setting the libc environment variable RCMD_CMD. This program would then run with an effective group ID of tty. Attackers could seize group tty privileges, which could allow them to interact with other user's terminals.





          Dropping Privileges Permanently


          Occasionally, application developers will make mistakes when writing the code for a program that permanently relinquishes its privileges. The following sample code represents part of a setuid root program:


              /* set up special socket */
          setup_socket();

          /* drop root privs */
          setuid(getuid());

          /* main processing loop */
          start_procloop();


          This code is similar in spirit to what you find in several common network programs. The program needs to be root to obtain a socket bound to a port below 1024 or to obtain a special socket for sniffing. The author wants the program to be safe and follow a least-privilege design, so after obtaining this socket, the program drops its root privileges by performing a setuid(getuid()), which sets the saved set-user-ID, the real user ID, and the effective user ID to the value of the real user ID.


          setuid(getuid()) is a common idiom for permanently relinquishing privileges, and it usually works without too many complications. However, in some situations, it's not enough, as explained in the following sections.



          Dropping Group Privileges

          Some programs are installed as both setuid and setgid, meaning they run with an elevated user ID and elevated group ID. The code in the previous section would be fine if the program is only setuid root, but if the program is setuid root and setgid wheel, the elevated group privileges aren't relinquished correctly. In the processing loop, the effective group ID of the process is still set to the privileged wheel group, so if attackers found a way to exploit the program in the main processing loop, they could gain access to resources available to that privileged group. The correct way to address this problem is to relinquish group privileges like this:


              /* set up special socket */
          setup_socket();

          /* drop root privs - correct order */
          setgid(getgid());
          setuid(getuid());
          /* main processing loop */
          start_procloop();


          This code drops the group permissions and then the user permissions. It seems fairly straightforward, but it can actually be done incorrectly, as shown in the following example:


              /* set up special socket */
          setup_socket();

          /* drop root privs incorrect order */
          setuid(getuid());
          setgid(getgid());

          /* main processing loop */
          start_procloop();


          This code doesn't fully work because the function calls are ordered incorrectly. The setuid(getuid()) function relinquishes root privileges. Remember that having an effective group ID of 0 doesn't mean you are a superuser, as superuser status is based solely on your effective user ID. The setgid(getgid()) call is performed with privileges of the nonprivileged user, so the result of the setgid(getgid()) call depends on the OS. In Linux, Solaris, and OpenBSD, only the effective group ID is modified, and the saved set-group-ID still contains the group ID of the privileged group. If attackers find a flaw in the program they could leverage to run arbitrary code, they could perform a setegid(0) or setregid(-1, 0) and recover the elevated group privileges.




          Dropping Supplemental Group Privileges

          Programs running as daemons can run into security issues related to dropping privileges that are a little different from setuid programs. This is because they are typically started as a privileged user and then assume the role of an unprivileged user based on user input. In this situation, you have to be cognizant of supplemental group IDs because if they aren't updated when privileges are dropped, they could leave the process with access to privileged resources.


          Certain implementations of the rsync application contained a vulnerability of this nature, which is detailed at http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-0080. If rsync runs as a daemon, it starts off with the user ID and groups of the user running the daemon (typically root). If the rsync daemon needs to operate as an unprivileged user, it runs the following code:


              if (am_root) {
          if (setgid(gid)) {
          rsyserr(FERROR, errno, "setgid %d failed",
          (int) gid);
          io_printf(fd,"@ERROR: setgid failed\n");
          return -1;
          }

          if (setuid(uid)) {
          rsyserr(FERROR, errno, "setuid %d failed",
          (int) uid);
          io_printf(fd,"@ERROR: setuid failed\n");
          return -1;
          }

          am_root = (getuid() == 0);
          }


          This code releases the effective group ID before the effective user ID, so it should drop those privileges in the correct order. However, this code doesn't drop the supplementary group privileges! The developers solved this problem by inserting the following code:


          #ifdef HAVE_SETGROUPS
          /* Get rid of any supplementary groups this process
          * might have inherited. */
          if (setgroups(0, NULL)) {
          rsyserr(FERROR, errno, "setgroups failed");
          io_printf(fd, "@ERROR: setgroups failed\n");
          return -1;
          }
          #endif
          ...
          if (setgid(gid)) {


          Note that setgroups() works only if you are the superuser and have an effective user ID of 0. This is another reason it's important to relinquish privileges in the correct order.





          Dropping Nonsuperuser Elevated Privileges

          As discussed earlier, the behavior of the setuid() and setgid() functions are different if the program isn't running as the superuser. setuid(getuid()) is a reasonable idiom for a program running as root that wants to drop privileges permanently, but if the effective user ID isn't 0, the same tactic yields system-dependant, and sometimes inadequate results.


          Say that the simple network program was changed so that instead of being setuid root and setgid wheel, it's setuid to another nonprivileged user, such as daemon. This might happen if you installed a kernel-hardening patch that let programs with a particular user ID or group ID allocate special sockets to avoid the root privilege requirement. The code would look the same:


              /* set up special socket */
          setup_socket();

          /* drop root privs */
          setgid(getgid());
          setuid(getuid());

          /* main processing loop */
          start_procloop();


          However, the semantics of this code would be quite different when not running with an effective user ID of 0. Both setgid() and setuid() would be called as nonprivileged users, and they would change only the effective IDs, not the saved IDs. (In FreeBSD and NetBSD, this code would change all three IDs, so it wouldn't be vulnerable.) Attackers who exploited a problem in the program could therefore regain any relinquished privileges. The solution for nonsetuid root applications that need to fully drop their privileges is to use the setresgid() and setresuid() functions or the setregid() and setreuid() functions if necessary. OpenBSD versions before 2.3 require two calls to setuid().


          A noted researcher named Michael Zalewski found a bug in Sendmail 8.12.0 (documented at www.sendmail.org/releases/8.12.1.html) that's a good real-world example of this situation. Sendmail used to install a set-user-ID root binary, but in version 8.12.0, it moved to a new configuration, with a set-group-ID smssp binary. Here's the code that is intended to drop the elevated group privileges:


          int
          drop_privileges(to_real_uid)
          bool to_real_uid;
          {
          int rval = EX_OK;
          GIDSET_T emptygidset[1];
          ...
          if (to_real_uid)
          {
          RunAsUserName = RealUserName;
          RunAsUid = RealUid;
          RunAsGid = RealGid;
          }

          /* make sure no one can grab open descriptors
          for secret files */
          endpwent();
          sm_mbdb_terminate();

          /* reset group permissions; these can be set later */
          emptygidset[0] = (to_real_uid || RunAsGid != 0)
          ? RunAsGid : getegid();

          if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
          {
          syserr("drop_privileges: setgroups(1, %d) failed",
          (int) emptygidset[0]);
          rval = EX_OSERR;
          }
          /* reset primary group and user ID */
          if ((to_real_uid || RunAsGid != 0) &&
          EffGid != RunAsGid &&
          setgid(RunAsGid) < 0)
          {
          syserr("drop_privileges: setgid(%d) failed",
          (int) RunAsGid);
          rval = EX_OSERR;
          }
          }


          First, setgroups() fails, but that's fine because the supplemental groups are ones for the real user, which is a nonprivileged account. setgid() successfully changes the effective group ID from the saved set-group-ID to the real group ID but doesn't fully drop the privileges (except in FreeBSD and NetBSD). The saved set-group-ID still has the privileged smssp group ID. The Sendmail developers fixed the issue by replacing the call to setgid() with conditionally compiled calls to setresgid() or setregid(), depending on which function is available.




          Mixing Temporary and Permanent Privilege Relinquishment

          Many applications designed to run in an elevated context are programmed by security-conscious developers who adopt a model of least privilegesrunning an application with the minimal set of privileges it requires at a certain time to achieve its objectives. This model often means running as the invoking user for the bulk of the program and temporarily switching to a more powerful user when a privileged operation is required. If no more privileged operations are required, often the application permanently relinquishes its elevated user-ID by using setuid(). Although this model is preferred for developing a privileged application, subtle errors can result in using setuid() when the effective user-ID has been changed previously, as shown in this example:


          #define STARTPRIV seteuid(0);
          #define ENDPRIV seteuid(realuid);

          void main_loop(void)
          {
          uid_t realuid=getuid();

          /* don't need privileges */
          seteuid(realuid);
          /* process data */
          ...
          STARTPRIV
          do_privileged_action();
          ENDPRIV
          /* process more data */
          ...
          /* done with root privs - drop permanently */
          setuid(realuid);
          /* process yet more data */
          ...
          }


          This code starts out by relinquishing its privileges temporarily with seteuid(realuid). When the program needs its root privileges, it uses the STARTPRIV macro to obtain them and the ENDPRIV macro to release them. Those macros work by calling seteuid(0) and seteuid(realuid), respectively. After a bit of processing, the program decides it wants to fully drop its privileges, and it does that with the common idiom setuid(realuid). The problem is that at this point, the effective user ID is the real user ID of the program, not 0. Therefore, setuid(realuid) doesn't affect the saved set-user-ID in most UNIX implementations, with FreeBSD and NetBSD being the major exceptions. If attackers find a way to co-opt the program after the final privilege drop and run a seteuid(0), they could recover root privileges from the saved set-user-ID.


          Here's another example:


          void temp_drop(void)
          {
          seteuid(getuid());
          }

          void temp_gain(void)
          {
          seteuid(0);
          }

          void main_loop(void)
          {
          ...
          while (options)
          {
          ...
          if (unsafe_option)
          {
          temp_drop();

          if (process_option()==END_OF_OPTIONS)
          goto step2;

          temp_gain();
          }
          ...
          }
          ...
          step2:
          /* drop root privs */
          setuid(getuid());
          ...
          }


          This code represents a simple set-user-ID root application. The main loop contains two steps: option processing and main processing. The option-processing code needs root privileges, but it temporarily drops them to process a potentially unsafe option. After the option-processing code is completed, the program enters step2, the main processing section. The rest of the code is complex and potentially prone to security issues, so it fully drops privileges with a setuid(getuid()) before continuing.


          The problem is that if an unsafe option signals that the option processing is prematurely complete, the jump to step2 happens while privileges are temporarily dropped. The setuid(getuid()) call succeeds, but it doesn't correctly clear the saved set-user-ID in the process, except in FreeBSD and NetBSD. Therefore, if there's an exploitable problem in the main processing code, users can reclaim root privileges by performing a seteuid(0), which succeeds because the saved set-user-ID is still 0.





          Dropping Privileges Temporarily


          Temporary dropping of privileges can also be difficult to implement correctly. Many of the pitfalls in permanent privilege relinquishment can be applied to temporary privilege changes as well. Furthermore, dropping group privileges (and supplemental group privileges) is an easy step to overlook. Finally, the order in which privileges are relinquished can cause some privileges to be retained mistakenly.



          Using the Wrong Idiom

          If you drop privileges temporarily, your program is still vulnerable to a low-level attack, such as a buffer overflow. If attackers can run arbitrary code within the context of your process, they can issue the necessary system calls to propagate a saved set-user-ID to the effective and real user ID fields and regain privileges. To avoid this possibility, dropping privileges permanently as soon as possible is the safest option for a setuid application.


          Tcptraceroute had a simple permission-related problem that a security specialist from Debian Linux named Matt Zimmerman discovered. The program intended to drop privileges permanently, but the author used the idiom for dropping privileges temporarily. Here's the vulnerable code:


             defaults();
          initcapture();
          seteuid(getuid());
          return trace();
          }


          This mistake was a simple one: The authors used the wrong function. They should have used setuid() rather than seteuid() to prevent privileges from being reclaimed later. Any memory corruption vulnerability that occurred in the application's trace() function could allow privileges to be regained simply by using seteuid(0). The full advisory is archived at http://freshmeat.net/articles/view/893/.




          Dropping Group Privileges

          Now take a look at a real-world example of a vulnerability related to dropping group privileges in the wrong order. (This vulnerability is documented in the FreeBSD security advisory FreeBSD-SA-01:11.inetd, which can be found at http://security.freebsd.org/advisories/FreeBSD-SA-01:11.inetd.asc.) The inetd server in FreeBSD contains code to handle the IDENT service, which remote users query to learn the user associated with any TCP connection on the machine. The service has an option thatallows users to place a .fakeid file in their home directory, which can contain a name the ident server provides instead of the real username. Because the ident server runs as root, the code in Listing 9-2 was used to drop privileges temporarily.


          Listing 9-2. Incorrect Temporary Privilege Relinquishment in FreeBSD Inetd





                /*
          * Here, if enabled, we read a user's ".fakeid" file in
          * their home directory. It consists of a line
          * containing the name they want.
          */
          if (fflag) {
          FILE *fakeid = NULL;
          int fakeid_fd;

          if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
          iderror(lport, fport, s, errno);
          /*
          * Here we set ourself to effectively be the user,
          * so we don't open any files we have no permission
          * to open, especially symbolic links to sensitive
          * root-owned files or devices.
          */
          seteuid(pw->pw_uid);
          setegid(pw->pw_gid);
          ...




          This code first calls seteuid() to take on the user's privileges. It then calls setegid() to take on the caller's effective group ID, but this call fails because the program has relinquished its superuser privileges.




          Using More Than One Account

          To understand this problem, consider a daemon that needs to use more than one user account. (This example is based on one provided by Jan Wolter, a software designer that wrote an interesting paper entitled "Unix Incompatibility Notes: UID Function Setting," available at www.unixpapa.com/incnote/setuid.html.) Here's an example of how it might be implemented:



          /* become user1 */
          seteuid(user1);
          process_log1();

          /* become user2 */
          seteuid(user2);
          process_log2();

          /* become root again */
          seteuid(0);


          The intent of this code is to do some processing as user1, and then assume the identity of user2 and do further processing. This implementation is flawed, however, because the call to seteuid(user2) fails because the program's effective user ID is no longer 0; it's user1. Correct code would have a seteuid(0) before the seteuid(user2) call.





          Auditing Privilege-Management Code


          Now that you have seen a variety of vulnerabilities in code running with special privileges, you can focus on a method for auditing how those privileges are managed throughout the application's lifespan. You can use the steps in the following sections to help you decide whether privilege management has been implemented correctly and adequately inhibits users' ability to exploit the application. You consider two main cases: an application that intends to drop privileges permanently and an application that intends to drop privileges temporarily.




          Permanent Dropping of Privileges

          Some programs run with root privileges and want to discard these root privileges permanently. When auditing an application that runs in a privileged context and you encounter this scenario, you need to address the following points:


          • Make sure the code that's going to drop privileges permanently is running with an effective user ID of 0. If it's not, it probably won't be able to drop privileges effectively. Look for possible unexpected code paths where the program might temporarily drop privileges and then permanently drop privileges without restoring temporary privileges first.

          • If supplemental groups are potentially unsafe, make sure they are cleared with setgroups(). Again, setgroups() works only when running with an effective user ID of 0.

          • Make sure the real group ID, the saved set-group-ID, and the effective group ID are set to an unprivileged group, usually done with setgid(getgid()). Look for code that mistakenly uses setegid() to try to drop privileges.

          • Make sure the real user ID, the saved set-user-ID, and the effective user ID are set to an unprivileged user, usually done with setuid(getuid()). Keep an eye outfor code that mistakenly uses seteuid() to try to drop privileges.

          • Make sure the privileged groups and supplemental groups are dropped before the process gives up its effective user ID of root. Otherwise, the program is likely to expose privileged group credentials.


          There are also programs that run without root privileges but want to discard one set of privileges permanently; for those programs, check the following points:


          • The programmer can't modify groups with setgroups(), as this function works only for superusers. If the program requires this functionality but doesn't have root privileges, it has a design flaw.

          • Programmers run into difficulty when using the setgid(getgid()) idiom because it probably leaves the saved set-group-ID set to a privileged group. You can suggest the use of setregid(getgid(), getgid()) or setresgid(getgid(), getgid(), getgid()), which sets all three group IDs to the real group ID. This method doesn't work in older versions of OpenBSD, however. You can instead suggest using setgid(getgid()) twice in a row to clear the saved set-group-ID.

          • Similarly, developers run into difficulty using the setuid(getuid()) idiom because it probably leaves the saved set-user-ID set to a privileged user. setreuid(getuid(), getuid()) or setresuid(getuid(), getuid(), getuid()) should work to set all three user IDs to the real user ID. This method doesn't work in older versions of OpenBSD, but you can instead suggest using setuid(getuid()) twice in a row.





          Temporary Dropping of Privileges

          If programs need to drop their privileges temporarily, check for the following:


          • Make sure the code drops any relevant group permissions as well as supplemental group permissions.

          • Make sure the code drops group permissions before user permissions.

          • Make sure the code restores privileges before attempting to drop privileges again, either temporarily or permanently.

          • Think about the consequences of changing the effective user ID for signals, debugging APIs, and special device files. These issues are discussed in more depth in this chapter and Chapter 10, "UNIX II: Processes." Signals are dealt with separately in Chapter 13, "Synchronization and State."




          Function Audit Logs for Privileged Applications

          As a useful auditing aid, you might find it advantageous to note in your function audit logs (described in Chapter 7, "Program Building Blocks") the privileges that each function runs with when auditing applications that switch privilege contexts. This is as simple as adding in an additional two entries for a function (See Table 9-5).


          Table 9-5. Function Audit Log Addition

          User Privileges

          RUID=user, EUID=root, SUID=root

          Group Privileges

          RGID=users, EGID=users, SGID=users, SUPP=users



          The example indicates both the user and group privileges in effect when the program is run. RUID, EUID, and SUID stand for "Real UID", "Effective UID", and "Saved set UID" respectively. The next row uses RGID, EGID, SGID, and SUPP to stand for "Real GID", "Effective GID", "Saved set GID", and "Supplemental Groups" respectively. You also need to add to your notes for the function if it changes privileges throughout the course of the function, and in which cases it will change privileges. This little addition to a standard audit log allows you to quickly and accurately assess whether resource accesses within the function are potentially dangerous or not.


          Note



          You saw that the privilege management API can behave differently on different UNIX systems, and, as such, you might not be able to correctly assess what the user and group privileges will be for a particular function. In this case, you also should make a note in the function audit log if non-portable privilege API usage might cause the application to behave differently on other OSs.







          Privilege Extensions


          The UNIX privilege model often comes under criticism because of its all-or-nothing design. If you're the root user, you have the unrestricted ability to wreak havoc on the system because you're granted access to any resource you want. To understand why this is a problem, return to one of the examples used in the discussion of user IDs. The ping program requires root privileges to run because it needs to create a raw socket. If a vulnerability is discovered in ping that is exploitable before it drops privileges, not only can users create a raw socket, but they can also modify any file on the system, potentially load kernel modules to hijack system functionality, delete log files, and steal sensitive data. So any program that needs to perform an operation requiring special privileges essentially puts the entire system's security at stake. Several technologies, discussed in the following sections, have been developed to combat this age-old problem.



          Linux File System IDs

          One set of IDs not mentioned previously is relevant to code running on a Linux system. In Linux, each process also maintains a file system user ID (fsuid) and a file system group ID (fsgid). These IDs were created to address a potential security problem with signals. If you recall, when a daemon running as root temporarily drops privileges to assume a user's role, it sets its effective user ID to the ID of the less privileged user.


          This behavior can lead to security issues because a process's effective user ID is used in security checks throughout the kernel. Specifically, it's used to determine whether certain signals can be sent to a process from another process. Because of this checking, when the daemon assumes the effective user ID of a local user on the machine, that user might be able to send signals and perhaps even attach a debugger to the daemon.


          To address this issue, the Linux kernel programmers created the fsuid and fsgid to be used for all file system accesses. These IDs are usually kept 100% synced with the effective user ID, so their presence doesn't affect use of the normal privilege-management APIs. However, a program that wants to temporarily use a normal user's file system privileges without exposure to attacks caused by security checks based on effective IDs can simply change its file system user and group IDs with the API calls setfsuid() and setfsgid().





          BSD securelevels

          The BSD securelevels kernel protection (now supported by Linux to some extent) is intended to protect the system kernel from the root user. The primary focus of securelevels is to enforce some restrictions on every user on the system, including the superuser, so that a root compromise doesn't render a machine completely vulnerable. It uses a systemwide kernel value, the "securelevel," to help decide what actions system users are allowed to perform. The different branches and versions of BSD vary in the number of levels they provide and the protection each level offers, but the idea is essentially the same in each version. The following excerpt from the init(8) man page describes the available levels:



          The kernel runs with four different levels of security. Any superuser process can raise the security level, but only init can lower it. The security levels are:


          -1 Permanently insecure modealways run the system in level 0 mode.


          0 Insecure modeimmutable and append-only flags may be turned off. All devices may be read or written subject to their permissions.


          1 Secure modethe system immutable and system append-only flags may not be turned off; disks for mounted filesystems, /dev/mem, and /dev/kmem may not be opened for writing.


          2 Highly secure modesame as secure mode, plus disks may not be opened for writing (except by mount(2)) whether mounted or not. This level precludes tampering with filesystems by unmounting them, but also inhibits running newfs(8) while the system is multi-user.


          If the security level is initially -1, then init leaves it unchanged. Otherwise, init arranges to run the system in level 0 mode while single user and in level 1 mode while multiuser. If level 2 mode is desired while running multiuser, it can be set while single user, e.g., in the startup script /etc/rc, using sysctl(8).



          As you can see, this systemwide setting can inhibit actions for even superusers. Although it offers a level of protection, it doesn't allow fine-tuning for specific processes and can be susceptible to bypasses by users modifying certain files and restarting the machine if they gain root access.




          Capabilities

          Linux has also undertaken the challenge of addressing the basic UNIX privilege shortcomings by implementing a technology known as capabilities. This model defines a set of administrative tasks (capabilities) that can be granted to or restricted from a process running with elevated privileges. Some of the defined capabilities include the following:


          • CAP_CHOWN Provides the capability to change the ownership of a file.

          • CAP_SETUID/CAP_SETGID Provides the capability to manipulate a user and group privileges of a process by using the set*id() functions discussed previously.

          • CAP_NET_RAW Provides the capability to use raw sockets.

          • CAP_NET_BIND_SERVICE Provides the capability to bind to a "privileged" UDP or TCP port (that is, one lower than 1024).

          • CAP_SYS_MODULE Provides the capability to load and unload kernel modules.


          Being able to grant and omit certain capabilities from applications makes it possible to create processes that have one special system capability without putting the entire system at risk if it's compromised. The ping program is a perfect example. Instead of having it run with full permissions to create a raw socket, it could be granted the CAP_NET_RAW privilege. If the program is compromised, attackers can create raw sockets (which is still a breach), but can't automatically load kernel modules or mount new file systems, for example.


          Capabilities are applied to running processes but can also be applied to files on disk to enforce restrictions or grant special privileges when a certain binary is run (much like the setuid/setgid bits associated with a file).


          A process has three bitmasks of capabilities:


          • Permitted set The set of capabilities the process can enable.

          • Effective set The set of capabilities that has been enabled already (the set that's consulted when performing a privileged operation).

          • Inheritable set The set of capabilities that a new process can inherit when the current process creates one.


          Although the effective set ultimately governs what a process can do, the other two sets are equally important. To see why, imagine that the ping program has only the CAP_NET_RAW capability in its effective set, but its permitted set includes a series of other random capabilities, such as CAP_SYS_MODULE. In this case, if users did compromise the ping program, they could enable the CAP_SYS_MODULE capability (thus adding it to the effective set) by using the sys_capset() system call and load kernel modules as a result.


          File capabilities are similar, except they're associated with a file. A file has three capability sets also, but these sets differ slightly:


          • Allowed set The set of capabilities that are allowed to be added to the process capability sets when the executable runs. (Capabilities apply only to executables.)

          • Forced set A set of capabilities that are granted in addition to those users might already have. This set allows a certain application to be given special privileges whenever it runs (like setuid/setgid bits on a file, but more refined).

          • Effective set This set isn't really a set, but a bit indicating whether capabilities in the permitted set should automatically be transferred to the effective set when a new process image is loaded.




          Capabilities Implementation Vulnerability


          In early incarnations of the Linux capabilities solution (Linux kernel 2.2.15), Wojciech Purczynski discovered an interesting logic error. Specifically, users were able to restrict their privileges to their eventual advantage. By removing the CAP_SETUID privilege from the inheritable and permitted privilege sets and then running a setuid root application, the application would run with root privileges but wasn't permitted to drop privileges if necessary. Therefore, a call to setuid(getuid()) would fail, and the application would continue to run in an elevated privilege context. An exploit was constructed that targeted Sendmail 8.10.1. You can read more details about this vulnerability at www.securityfocus.com/bid/1322/discuss.
















          Absolute OpenBSD - UNIX for the Practical Paranoid












          Absolute OpenBSD—UNIX for the Practical Paranoid

          by Michael W. Lucas









          San Francisco




          All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher.



          Printed on recycled paper in the United States of America


          1 2 3 4 5 6 7 8 9 10 - 06 05 04 03


          No Starch Press and the No Starch Press logo are registered trademarks of No Starch Press, Inc. Other product and company names mentioned herein may be the trademarks of their respective owners. Rather than use a trademark symbol with every occurrence of a trademarked name, we are using the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark.



          Publisher: William Pollock



          Managing Editor: Karol Jurado



          Cover and Interior Design: Octopod Studios



          Copyeditor: Kenyon Brown



          Compositor: Wedobooks



          Proofreader: Stephanie Provines


          Distributed to the book trade in the United States by Publishers Group West, 1700 Fourth Street, Berkeley, CA 94710; phone: 800-788-3123; fax: 510-658-1834.


          Distributed to the book trade in Canada by Jacqueline Gross & Associates, Inc., One Atlantic Avenue, Suite 105, Toronto, Ontario M6K 3E7 Canada; phone: 416-531-6737; fax 416-531- 4259.


          For information on translations or book distributors outside the United States, please contact No Starch Press, Inc. directly:


          No Starch Press, Inc.
          555 De Haro Street, Suite 250, San Francisco, CA 94107
          phone: 415-863-9900; fax: 415-863-9950; <info@nostarch.com>; http://www.nostarch.com


          The information in this book is distributed on an "As Is" basis, without warranty. While every precaution has been taken in the preparation of this work, neither the author nor No Starch Press, Inc. shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in it.



          Library of Congress Cataloguing-in-Publication Data





          Lucas, Michael W., 1967-
          Absolute OpenBSD: UNIX for the practical paranoid / Michael W. Lucas.
          p. cm.
          Includes index.




          ISBN 1-886411-99-9





          1. OpenBSD (Electronic resource) 2. Operating systems (Computers) 3. UNIX (Computer file) I. Title.


          QA76.9.O63L835 2003


          005.4'32--dc21


          2003000473



          For Elizabeth, who brings spring's rich warm sunlight into darkest night




          ACKNOWLEDGMENTS


          OpenBSD is quite a trip, and the OpenBSD community even more so. Since starting this book, I've talked with more practical and professional paranoids than I knew existed outside of politics. It's been my privilege to work with some of the best computer security people in the world. Best of all, these people care about their work, and the impact it has on average people such as our parents and friends.


          The following people all provided feedback on one or more chapters of this book, or answered specific questions on frequently-misunderstood aspects of OpenBSD, and as such deserve my heartfelt thanks. Some of them are OpenBSD crown princes, and others are just users who were trying to figure out what their computer was actually doing. What I've done right is thinks to them, and what I've done wrong is my own fault. They are, in alphabetical order: Shawn Carroll, Chris Cappuccio, Dave Feustel, Thorsten Glaser, Daniel Hartmeier, Jason Houx, Volker Kindermann, Anil Madhavapeddy, U.N. Owen (aka dreamwvr), Francisco Luis Roque, Srebrenko Sehic, Matt Simonsen, Sam Smith, Duncan Matthew Stirling, Peter Werner, and Jason Wright.


          A special thanks goes out to Theo de Raadt, for taking time out of his fiendishly busy schedule to provide special insight into the innards of OpenBSD, for not holding back when I goofed, and especially for sticking to his standards of freedom, despite everything the world has to say on that subject.


          When an author says something like, "Hold the presses! OpenBSD just added a whole slew of functionality and I have to rewrite huge sections of the book you were planning to ship out tomorrow," the editor is supposed to respond with dire threats involving chainsaws. The folks at No Starch just say, "Well, get to work then." I have been forced to report to the Secret Author Cabal that Bill and Karol are patient, kind, and thoughtful enough to resist our best techniques for driving publishers into Lovecraftian madness.


          Then there's Sifu Brown and the fine staff and volunteers of the School of Chinese Martial Arts in Berkley, Michigan (http://www.ZenMartialArts.com). They have absolutely nothing to do with computers, but they have an awful lot to do with me not making dire threats involving chainsaws. Somehow, the Five Ways to Become a Great Martial Artist turned out to be the same as the Five Ways to Write a Great Computer Book — I just never knew it before.


          And finally, for Liz, and not just for catching the pet rats before they can stash seeds in server cases.



          Michael Lucas


          Saint Clair Shores, MI


          May 2003













          Intrusion Detection














          Intrusion Detection



          Intrusion detection has its roots in the financial audits of mainframe computers. The scarcity and expense of mainframes meant that access to their computing power had to be tightly controlled (and users had to be accurately billed). In the late 1970s, financial audits were adapted for security purposes, enabling administrators to review logs for anomalies that might indicate misuse, such as unauthorized file changes.


          Further developments led to real-time detection capabilities in the mid-1980s, and to network monitoring in 1990. Today, network managers can choose from a variety of solutions and vendors, but the general principles of an Intrusion Detection System (IDS) remain the same.


          The idea behind an IDS is simple: an agent monitors file activity on a host or traffic on a network, and reports strange behavior to an administrator (see Figure 1).






          Figure 1: Electronic Eyes. An Intrusion Detection System (IDS) monitors network traffic or file activity on a host for attacks, anomalous behavior, and misuse. An IDS logs intrusions, sends real-time alerts, and in some situations can halt the attack.

          The IDS market is divided into two primary groups: host-based and network-based systems. This tutorial examines the basics of host- and network-based detection systems, discusses how to implement an IDS, and outlines a proper incident-response plan.




          Host-Based IDS


          Host-based IDSs add a targeted layer of security to particularly vulnerable or essential systems. An agent sits on an individual system-for example, a database server-and monitors audit trails and system logs for anomalous behavior, such as repeated login attempts or changes to file permissions. The agent may also employ a checksum at regular intervals to look for changes to system files. In some cases, an agent can halt an "attack" on a system, though a host agent's primary function is to log events and send alerts.


          The primary benefit of a host-based system is that it can detect both external and internal misuse, something that network monitors and firewalls can't do. The appeal of such a tool is obvious, as security breaches are more likely to come from an internal user than from a hacker outside the network. Host agents are powerful tools for addressing the authorization and access issues that make internal security so complex.


          Agents install directly on the host to be monitored, so they must be compatible with the host's OS. Memory requirements and CPU utilization will vary from vendor to vendor, so be sure to learn ahead of time the demands the agent will place on the system.






          Network-Based IDS



          A network-based IDS sits on the LAN (or a LAN segment) and monitors network traffic packet by packet in real time (or as near to real time as possible), to see if that traffic conforms to predetermined attack signatures. Attack signatures are activities that match known attack patterns. For example, the TearDrop Denial of Service (DoS) attack sends packets that are fragmented in such a way as to crash the target system. The network monitor will recognize packets that conform to the TearDrop signature and take action.


          The IDS vendor provides a database of attack signatures, and administrators can also add customized signatures. If the IDS recognizes an attack, it alerts an administrator. In some cases, the IDS can also respond, for example by terminating a connection. In addition to its monitoring and alarm functions, the IDS also records attack sessions for later analysis. Network IDSs can also be linked to other security features, such as firewalls, to make sure those systems haven't been breached.


          A network monitor has two main benefits. The first is the real-time nature of the alarm, which can give administrators an opportunity to halt or contain an attack before it does significant harm. This is especially valuable for DoS attacks, which must be dealt with immediately to mitigate damages.


          The second benefit is evidence collection. Not only can administrators analyze the attack to determine what damage might have been done, the attack session itself can point out security flaws that need addressing. (This is also true for host-based systems). Because many hackers first scan a target network for known vulnerabilities, a hacker's choice of attack may indicate that such vulnerabilities exist on your network. A simple example is an operating system that has yet to be secured with the latest vendor patch.


          Network monitors are OS-independent. Basic requirements include a dedicated node that sits on the segment to be monitored and a NIC set to promiscuous mode. You may also want to set up a secure communications link between the monitor and its management console.






          Establishing An IDS



          The first step in establishing an IDS is to incorporate it into your security policy. (If you don't have a security policy, now would be a good time to develop one. See "Create Order with a Strong Security Policy," Network Magazine July 2000, page 62 for more information.) In brief, a security policy defines the basic architecture of the network, describes how the network will be secured, and establishes a hierarchy of user access to data resources.


          When incorporating an IDS into your security policy, you should define how the IDS will fit into the overall security architecture, outline procedures for maintaining and responding to the IDS, and assign resources (software, hardware, and humans to manage the technology).


          You'll also have to choose a network- or host-based system, or a combination of both. A combination provides the most comprehensive security; however, this decision will be colored by the level of security you require, the budget at your disposal, and the in-house resources on hand to manage the system.


          Generally speaking, network monitors cost significantly more than host-based agents. However, depending on the size of your network, a single monitor can offer substantial network coverage. Conversely, host-based agents cost less, but are limited to a single host.


          Other factors play in deciding to implement either or both solutions. For example, network monitors may have difficulty with encrypted traffic. A network monitor functions by reading packet headers and data payloads. If this information is encrypted, the IDS can't detect attacks. Encryption doesn't hinder host agents because the data is decrypted before a host agent sees it.


          Network sensors can also become a bottleneck on high-speed LANs, degrading performance and frustrating users. According to an ICSA paper, a network-based IDS can handle up to 65Mbits/sec of traffic before the analysis engine's performance drops (see Resources).






          Care And Feeding


          Regardless of which solution you implement, you must be prepared to properly maintain the system. IDSs can generate reams of data that have to be reviewed regularly. Most products come with reporting software to help with this task.


          Depending on the number of monitors and agents you employ, be prepared to dedicate substantial storage to IDS log files. You also must secure audit logs so that intruders can't tamper with them to erase or obscure evidence of the intrusion.


          Like anti-virus products, an IDS's attack signature database must be updated regularly. Vendors will provide new attack signatures, but be sure to query them on the frequency of updates, especially in response to newly discovered attacks. Be aware that slight variations to a known attack may be enough to slip past even an IDS with the most current signatures.


          You can also be proactive by monitoring security sites for new attack signatures and exploits.






          Dealing With Incidents


          Once you install your IDS, be prepared for the possibility that it will work! That is, have a plan in place for dealing with intrusions once you detect them.


          The first step is to create an incident-handling team to respond to intrusions. The size and capabilities of your team will vary with the size of your organization, but each member of the team should have clearly-defined roles and responsibilities (for example, a Windows NT specialist, a Unix specialist, and so on).


          You'll also want to create an incident-handling policy that outlines the response procedures and lists contact information for team members. Procedures include backing up an affected hard drive and determining whether it is necessary to enlist outside expertise or contact law enforcement agencies.


          The decision to involve the law is a complicated one, so it's best to have policies in place beforehand. It may not be worth your time to call the cops on a script kiddy who Pings your network.






          Is An IDS Right For You?


          Intrusion detection is still a maturing technology, and not everyone in the security community is convinced of its viability. Some observers have compared intrusion detection to the so-called Star Wars missile defense program-that is, expensive and ineffective.


          Of course, "expensive" is a relative term: While an IDS doesn't run cheap, the cost of a network outage from a DoS attack (and the attendant bad press, dissatisfied customers and business partners, and furious executives) can easily justify the vendor's price tag.


          As for effectiveness, an IDS is not a "set it and forget it" proposition. Security policies must be in place, attack signature databases must be updated, and logs have to be reviewed regularly to gain the full benefits. If you can meet those requirements, intrusion detection is a valuable tool for protecting your data resources.






          Resources


          The ICSA's Intrusion Detection Systems Consortium has an informative white paper on Intrusion Detection Systems (IDSs). You can download the paper at www.icsa.net/html/communities/ids/White%20paper/index.shtml.



          Intrusion Detection by Rebecca Gurley Brace (Macmillan Technical Publishing, 2000) is an informative and comprehensive guide to the concepts and principles of IDSs.


          The IDS vendor Network Security Wizards has a good collection of papers and articles at www.securitywizards.com/library.html. Network managers may be particularly interested in the rebuttal to "50 Ways to Defeat Your Intrusion Detection System." CERT maintains an IDS checklist at www.cert.org/tech_tips/intruder_detection_checklist.html.



          Network Magazine has published several useful articles on IDSs. Check out "Deploying an Effective Intrusion Detection System" (September 2000, page 60), "Security Reality Check" (July 1999, page 80) and "Can Intrusion Detection Keep an Eye on Your Network's Security?" (April 1999, page 36). In addition, the article "Incident Handling" (January 2000, page 80) provides a good overview of how to build an incident-handling team. Archived article can be found at www.networkmagazine.com.




          This tutorial, number 149, by Andrew Conry-Murray, was originally published in the December 2000 issue of Network Magazine.