Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 84 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
84
Dung lượng
6,75 MB
Nội dung
across multiple machines, which has both advantages and disadvantages. DIPC is not available on other platforms (kernel mods are required). Programs that use DIPC may also have to deal with byte ordering problems. Named pipes rely on the existence of a separate fifo object in the filesystem for each possible simultaneous connection, and probably do not queue connection requests; it appears that they honor file permissions. Psuedottys can probably be used as an IPC mechanism. A variety of signals may be delivered. Filesystem permissions are checked but the filesystem objects are shared with other programs like telnetd, and so permissions need to be set at runtime by a process running as root. You need a way to tell the other process which psuedotty pair to use. TLI allows access to uids/gids but apparently it isn’t very well documented. TLI is not that widely available and may not even be available on Linux. The identd Daemon The identd daemon runs on computers and may be polled to determine the name of the user who owns a particular open TCP/IP connection using the RFC 1413 protocol. The data returned by identd is not necessarily trustworthy but is still useful. Only trust the data from an identd server for authentication purposes if you trust the security of the machine and the integrity of anyone who has root access. identd is typically used to identify users and log that information. If there is trouble, that information may help narrow down the blame. Of course, anyone can run a bogus identd server on any machine they control. That is actually not the weakness it appears to be because after an attack your action will be to deny access to the smallest group pos- sible, which includes the attacker. If you can narrow it down to a particular user you deny access to that user; otherwise, you deny access to the entire machine or even an entire network. If you accept connections from the localhost or other secure machines on your local net- work, identd on those machines can usually be trusted to the same degree the machines themselves are and may be used to help authenticate users. identd queries may be vul- nerable to various “man in the middle” and TCP hijacking attacks, which should be taken into account. Use of identd in this manner also helps validate that local TCP connec- tions have not been spoofed; if that is the case, your identd daemon on the other side is likely to return an error that the connection in question does not exist on that machine. The attacker will have to work harder to spoof the original connection and the identd one as well. Your firewall should be configured to prevent outsiders from spoofing or hijacking TCP connections between internal hosts. Special Programming Techniques P ART V 674 4172316072 CH35 7/26/99 2:27 PM Page 674 Beware, however, of ssh port forwarding or IP masquerading and identd. And take pre- cautions to insure that the response you received actually originated on the local host. A secret token of some sort is advisable to further verify the local origin and identity of a TCP/IP connection. This token may be stored, for example, in a file accessible only by a particular user. TCP Versus UDP This may ruffle a few feathers, but I don’t recommend using UDP for anything because of its serious security drawbacks. TCP is vulnerable to DOS via open connections in which the data transfer is never completed, but in almost every other security aspect it is superior to UDP. UDP has some performance advantages that make it popular for appli- cations such as remote filesystem access and real-time video and audio. Yet even those applications tend to have TCP versions. As a programmer, if you implement a UDP ver- sion you will probably have to supplement it with a TCP version to get through firewalls. You might as well start with the TCP version; there likely won’t be a compelling reason to implement UDP. TCP is much less vulnerable to source address spoofing than UDP due to the three-way handshake. TCP is vulnerable to hijacking but with UDP you don’t even need to hijack. TCP sessions can be authenticated once per session at the application level. A firewall only needs to validate a TCP connection when it is first opened and can tell what direc- tion the connection is being made from, which is vital information to a firewall. There is no direction information for UDP; a firewall can’t tell if an arbitrary UDP packet is an inbound request (which should usually be blocked) or an inbound reply (which may need to be allowed). Most firewalls block almost all UDP traffic and that which they don’t block is very vulnerable to misuse. Any application that does use UDP should probably reject any packets that originate from certain well known ports, particularly the one used by DNS; those packets are prob- ably attacks that snuck through a firewall. The popularity of UDP for real-time audio and video is primarily due to the fact that if a packet doesn’t arrive there is no point in transmitting it. TCP connections may be used to for these applications with appropriate throttling algorithms, which limit the amount of data transmitted to what the link can sustainably handle. UDP applications also should throttle the data or quality will suffer. The only real difference is that a few extra retrans- mitted TCP packets may use up bandwidth when the throttling is not perfect. Multicast applications are stuck with UDP; these applications will be completely unusable across most firewalls. Secure Programming C HAPTER 35 675 35 SECURE PROGRAMMING 4172316072 CH35 7/26/99 2:27 PM Page 675 Dynamic Versus Static Memory Allocation Allocation of fixed size buffers can cause some inconvenience by imposing hard limits, but those limits are likely to be well known and clients can try to avoid them in advance. Differential truncation or residual data on a line after reaching a hard limit can cause problems as mentioned previously. Programs that use dynamic memory are prone to memory leaks and fragmentation. Memory leaks are often not a big problem in short lived utility programs but they can cause problems in preforked server processes, which may normally live for days. As a guard against memory leaks, I normally make the process check the number of requests it has serviced and its total memory usage between requests, then have the process die and be replaced by a fresh process if either limit has been exceeded. The first check keeps minor leaks from consuming more memory than needed and the second stops fast leaks quickly. Programs that use dynamic memory can also be the targets of DOS attacks that exhaust available system memory. Setting process limits with setrlimit() can help prevent total exhaustion of system resources but it may result in the process being terminated without warning; this could occur in an unexpected and inconvenient place, which could leave unfinished business. Having the process monitor its own usage can give it the opportuni- ty to die more gracefully, but it may not always catch the problem in time unless the checks permeate all code that reads or copies data. Buffer overflows in dynamically allocated memory can corrupt the memory pool itself; creative use of this might even permit an attacker to write to any arbitrary section of memory. Programmers who make heavy use of dynamic memory often allocate a new memory block after measuring the length of a string that will be copied; this is resistant to buffer overflows on copies but other techniques need to be used when reading from a data stream. Use of stale pointers is a common problem in programs that use dynamic memory and can result in crashes, unpredictable behavior, and other buffer overflows or values myste- riously appearing in the wrong variable. It is helpful in programs that use either type of memory allocation scheme to include a distinctive signature field in each type of data structure. This will make it easier to detect many cases where a pointer points at the wrong data. Programs that use dynamic memory, with or without using setrlimit() or other limit- ing techniques, still have hard limits, despite the rhetoric to the contrary; the limits are just larger and often less predictable. I prefer to avoid dynamic allocation in most cases and to exercise care when I do use it. Both schemes require careful usage. Special Programming Techniques P ART V 676 4172316072 CH35 7/26/99 2:27 PM Page 676 Security Levels The security level features implemented in newer kernels allow you to designate certain files as immutable or append only. While you cannot really use those from your own pro- grams, they can be used to restrict access to important files, which may help reduce the damage caused by some exploits. POSIX.1e Capabilities POSIX.1e style capabilities being developed (Linux-Privs Project) will allow much finer control of the privileges a program has. Instead of very course controls, such as being root and not being root, this will allow fine tuning of exactly which privileged system calls a program can execute. By giving up all privileges that are not needed, a program can minimize the effects of many exploits. A program that only needs the ability to bind to a restricted socket, for example, can give up others. Unfortunately, many daemons require the setuid() family of calls and I don’t know of any plans to separate the ability to switch between various ordinary uids (nor- mal users) and switching to privileged ones (root, bin, etc.). This would be very handy for the many daemons that access a user’s files on their behalf. If you have write access to root’s files, you can easily gain root privileges. Erasing Buffers If you use some form of binary communications with other processes you should be careful to erase buffers, particularly the portion of strings after the terminating null, to insure that you do not accidentally divulge sensitive information. Many years ago, I wrote a program that allowed me to obtain the plaintext of passwords from a NetWare server immediately after they had been accessed by someone else in another building. The server did not clear its buffers and always sent a reply of a certain minimum length; my program simply issued a system call that would fail and all but the first couple bytes of the previous request’s response were returned unmodified. HTML Form Submissions Past Firewalls Web browsers on machines inside your firewall can be tricked by outside Web sites into transmitting arbitrary text to internal TCP based services (by specifying the target service in the ACTION attribute of the FORM tag). Some encodings permit more flexibility in attacks than others. Many browsers will block a few well-known services from being the target of form submissions. This deny known bad approach is problematic. All text-based Secure Programming C HAPTER 35 677 35 SECURE PROGRAMMING 4172316072 CH35 7/26/99 2:27 PM Page 677 (and possibly some binary encoded) TCP services other than HTTP should look for strings typically sent by Web browsers such as GET, PUT, POST, Content-Type:, Content- Length:, Referrer:, and Accept:. If any line received begins with one of these strings, log an attempted security violation (preferably along with the Referrer: field) and ter- minate processing of data and commands on that connection. Snooping, Hijacking, and Man in the Middle Attacks When writing servers and clients, you should keep in mind that it is possible for an attacker to modify or take over an existing TCP connection. UDP services are much more vulnerable. A “man in middle” attack can take over a TCP connection or even mod- ify it in transit. This is easier if the attacker controls a machine between the two systems, or is at least attached to a network segment that the packets travel over. Hijacking attacks can be done remotely without control over the intervening network. Hijacking attacks normally involve guessing the sequence numbers on packets and interjecting packets with the right sequence number. A vulnerability recently reported in the Linux TCP/IP stack allows hijacking without needing to guess the sequence number; this will probably be fixed fairly quickly but many systems will still be running older kernels. Snooping (a.k.a. sniffing or eavesdropping) on packets in transit is common. This is par- ticularly common on ethernet segments where a compromised machine is used to snoop some or all packets on the ethernet segment. Every machine on a typical ethernet can intercept packets travelling between any two machines on that ethernet segment, includ- ing packets that are transiting the local network on their journey between two other net- works. An attacker who has broken into your ISP’s Web server may be able to see every packet that travels in and out of your Internet connection. Hubs do nothing to protect against this. Switches, bridges, routers, and firewalls reduce the vulnerability to snooping and spoofing by reducing the network into smaller segments. Many of these devices are not designed with security in mind, however, and can be tricked to allow sniffing or spoofing; choose and configure your network hardware carefully. Particularly sensitive communications should require periodic re-validation, particularly before critical transactions such as those that would cause funds to be dispersed. Encryption is strongly recommended for sensitive communications. Any security sensitive internal communications should be behind a firewall. Among other checks, the firewall should make sure that no outside host can spoof (masquerade as) any internal host, including localhost (127.0.0.1). This will make spoofing or hijacking of internal connections considerably more difficult but cannot protect connections where one party is outside. Special Programming Techniques P ART V 678 4172316072 CH35 7/26/99 2:27 PM Page 678 HTML Server Includes If your Web server has the server include feature enabled, there are a number of security implications, depending on your server version and configuration. An ordinary user on the system (or anyone who can install Web pages) may be able to use server includes to read an otherwise restricted file. Even worse, they may be able to execute arbitrary pro- grams on the machine. Many CGI programs can be tricked into including server include tags in their output, which may be parsed by the server, allowing any remote user to do the same things. The server include tags may be included in form input, cookies, or the URL and may inadvertently be echoed by a CGI script. Preforked Server Issues Preforked servers, where a server spawns a fixed number (or limited range depending on load) of child processes in advance to handle incoming connections, are very efficient but have some special considerations. The code that manages the number of child processes often actually gives the server some protection against DOS attacks, which would cause the spawning of large numbers of processes. Remote users will have as much or more trouble getting through but the operation of the local machine is less likely to be disrupt- ed. Since preforked server processes often handle many different transactions, care must be taken to ensure that an attacker cannot influence the internal state in a way that could affect the processing of subsequent transactions. You must also ensure that an attacker cannot obtain information still stored in internal data structures about past transactions. Erasing data structures and the stack (use a recursive function that calls itself many times and initializes a large automatic variable with zeros) will help. Memory leaks will be more of a problem in preforked servers and other long-lived processes. Timeouts Any network client or server should have configurable timeouts with defaults appropriate to the application. In server processes, I normally set a timer using alarm() or setitimer() at the beginning of each transaction. The signal, if it arrives, must be han- dled appropriately. In the event that a DOS attack is detected, it may be appropriate to dramatically reduce the timeouts for the duration of the attack; this will adversely affect some users but will probably improve service for most. Secure Programming C HAPTER 35 679 35 SECURE PROGRAMMING 4172316072 CH35 7/26/99 2:27 PM Page 679 Three Factor Authentication Authentication of users is normally based on one or more of the following three factors: biometric, knowledge, or possessions. Biometric factors are things such as signatures, fingerprints, retina scans, and voice prints. The second factor is based on something you know, usually a secret password, key, or token. The third is based on something you have in your possession, such as a smart card, a ring, a credit card sized one time password or time-based password generator, or a pregenerated list of challenge responses. For high security applications, it is advisable to use all three. There is an interesting device called the Biomouse, which is a small fingerprint reader. A newer version also includes a smart card reader in the same unit. Linux drivers are avail- able. Many of these newfangled gadgets can be useful but they all have their limitations. Ignore all the sales hype that says they are invulnerable. Smart cards should have onboard encryption processors, not merely be memory-based devices. Even then there is an impressive array of technology available to attack smart cards (or any other integrated circuit-based security device). Biometrics can be faked or recorded. You leave thousands of copies of your fingerprints every day that could be used to reconstruct your finger. The Biomouse senses pores as well as ridges, so a latent print may not be sufficient to recon- struct the necessary image but there can be other sources of that data, particularly from the biometric devices themselves. The fingerprint scanner used by a merchant down the street when one of your employees cashed a check or the elevator button that is really a clandestine scanner may be the key to fooling your fingerprint scanners. Voice prints can be recorded. The most common time-based token generator cards are based on old undocumented algorithms that would not be likely to withstand a serious reverse engi- neering attack. In extreme cases, biometric devices may be defeated by forcing an autho- rized user to allow access, or by very sophisticated mimicking devices. The data from these gadgets may be vulnerable to interception on a compromised workstation or net- work. These vulnerabilities are also a good reason to use two or three factor authentica- tion. Gadgets can help to improve security, but beware of a false sense of security. Pluggable Authentication Modules Linux has borrowed the Pluggable Authentication Module (PAM) concept from Sun Microsystems. These separate out the authentication code from the application and put it in some shared library modules, which may be configured as needed for various sites. PAM can be configured to use /etc/passwd, shadow passwords, one-time passwords, Special Programming Techniques P ART V 680 4172316072 CH35 7/26/99 2:27 PM Page 680 zero knowledge systems such as SRP, remote authentication servers, and biometric sys- tems such as the Biomouse. Other PAM modules check whether a user is on a local con- sole or a remote terminal. Configuration files edited by the system manager instruct PAM on which modules to use for which applications and under which circumstances. Applications that use PAM need to be modified to use the PAM API and should to be prepared to act as the middleman for a more complicated dialog than simply asking for a username and password. The PAM guides are available online at http://www.sics.se/~pelin/pam/ and include a sample program. Changing passwords is supported by PAM but changing other fields in /etc/passwd and /etc/shadow is not. Use the putpwent() function or see the sample program in Appendix A, “A Symbol Table Library,” for an example of how to access the file directly. If you want your program to be portable to systems that do not have PAM, I suggest writing a dummy PAM library that calls getpw() or getpwent() and crypt(), or try porting libpwdb to the target system. If you are not using PAM or libpwdb, you may need to deal with shadow passwords and NIS. General Program Robustness It is important to write security sensitive programs to be as robust as possible. Many security exploits depend on the fact that programs behave in unexpected ways in response to unexpected circumstances. Check return values from functions. Check errno after using functions that set errno (such as some math functions). Make extensive use of assert() to verify assumptions. Functions that take pointers should do something sensible with NULL values. Functions that take a size parameter, giving the size of a string parameter which will be modified, should handle a size of zero or even a negative size. Cryptography This section will be brief and skimpy on the technical details due to export laws that make export of cryptographic hardware, software, or technical advice illegal, although a book is probably the safest way to export technical information on cryptography. Encryption is vital to safeguarding sensitive information, particularly in transit. Cryptography is used to ensure the privacy of information in transit or storage, authenti- cate users and messages, and prevent repudiation (denying that you originated a message later when it proves inconvenient). Secure Programming C HAPTER 35 681 35 SECURE PROGRAMMING 4172316072 CH35 7/26/99 2:27 PM Page 681 Small or large licenses for use of RSA’s encryption software, RSAref, in commercial software (including the underlying algorithms) in clients and servers are obtained from Consensus (http://www.consensus.com); there are some inconvenient terms regarding reverse engineering. The cost per copy is significantly higher than the high volume licenses available to large software companies, but this is probably the most viable option for small software companies. SSLeay (see the following section, “Encryption Systems”) can be compiled to use RSAref. If you want to use the various free applica- tions in a commercial environment, it is possible to obtain licenses to the RSA algorithm for all free software on a given machine for price per machine; Information on this can be found at http://www.rsa.com. Types of Cryptographic Algorithms Beware of any encryption system that relies on an unpublished algorithm. These are usu- ally extremely weak and might not even keep your kid sister out. Hash functions provide one way (irreversible) encryption. These are commonly used for password storage, to generate one time passwords, and as file signatures used to deter- mine whether a file has been altered. Although you cannot decrypt a hashed password, you can encrypt passwords supplied by the user and compare the hash values. Examples of hash algorithms are MD5, MD2, MD4, SHA, Tiger, and RIPE-MD-160. Hash func- tions can also be turned into symmetric ciphers. Symmetric cipher algorithms use the same key to both encrypt and decrypt information. DES, 3DES, Blowfish, Twofish, RC4, and SAFER are examples of symmetric algo- rithms. Some of these algorithms are limited to fixed key lengths; others allow variable key lengths. One problem with symmetric algorithms is key distribution. Both the sender and recipi- ent need to have the same secret key. Asymmetric cipher algorithms use two separate keys, one public and one private. The public key can be made readily available. A docu- ment encrypted with the public key can only be decrypted with the private key and vice versa. Because these algorithms are normally much less efficient than symmetric sys- tems, asymmetric systems are normally used to exchange a unique secret session key, which is then used to encrypt the actual data stream using a symmetric algorithm. Asymmetric systems normally require much longer key lengths to achieve the same level of security. 1024 bits is commonly used for a symmetric system. RSA, ElGamal, and LUC are public key (asymmetric) systems. Elliptic curves are also used. Digital signatures use public key encryption and/or hashes to permit verifying the origi- nator and/or content of a message and prevent repudiation of a message. X.509 certifi- cates are essentially specially formatted and digitally signed binary documents that attest Special Programming Techniques P ART V 682 4172316072 CH35 7/26/99 2:27 PM Page 682 to the identity of a person, company, or Web server. The PGP encrypted mail system relies on key signatures, which are similar to certificates, to validate the association of a particular public key with a particular person or organization. Although certificates are usually issued by a central authority, key signatures are usually issued by specific indi- viduals and each user of PGP decides whose signatures to trust; this forms what is referred to as a “web of trust”. Cryptographically secure pseudo-random number generator algorithms are an important part of many encryption systems, particularly public key systems. These are used for many purposes, including generating the unique session key, initially generating the pri- vate and public key pairs, and generating unique tokens for use a tokens for Web sites. RFC 1750 has recommendations for random number generators (ftp://ftp.isi.edu/in-notes/rfc1750.txt). Zero knowledge systems can provide more security for user authentication without using algorithms that could encrypt data, and thus be subject to export controls. Stanford’s SRP (Secure Remote Password) provides zero knowledge based password verification, which is not vulnerable to eavesdropping. Optionally, exchange of a session key for session encryption and a PAM module, and modified telnet and ftp programs are available. More information is available at http://srp.stanford.edu/srp/. A more detailed overview of the different algorithms can be found on the Internet at http://www.ssh.fi/tech/crypto/algorithms.html. Bruce Schneier’s book Applied Cryptography (2nd Edition, John Wiley & Sons, 1995) is the standard text in the field. This book is actually exportable but the floppy containing the software included as list- ings in the book is not. The cryptography FAQ, at http://www.cis.ohio- state.edu/hypertext/faq/usenet/cryptography-faq/, has more additional informa- tion, and most of the Web sites mentioned in this section will have links to other sites of interest. Most ciphers are block oriented; they encrypt a block of some number of bits at a time and the operation is repeated until the whole stream is encrypted. Block chaining is fre- quently used so that the results of one block affect the operation of the next block. The block length is often equal to the key length. Encryption Systems The SSL algorithm is a public key based system introduced by Netscape and used to encrypt arbitrary TCP/IP data streams, and is widely used for encryption of Web pages. SSLeay is a very popular implementation of SSL. It can be downloaded from ftp://ftp.psy.uq.oz.au/pub/Crypto/ and ftp://www.replay.com. These Web sites Secure Programming C HAPTER 35 683 35 SECURE PROGRAMMING 4172316072 CH35 7/26/99 2:27 PM Page 683 [...]... r-drwxr-xr-x 1 1 1 1 kwall kwall kwall kwall users users users users 11451 1040 96 56 732 2 1 1 2 kwall kwall kwall kwall users users users users 1024 10 3256320 1024 Apr Apr Apr Apr 19 19 23 18 199 7 199 7 199 7 199 7 ABOUT-NLS AC-PATCHES AM-PATCHES AUTHORS Apr 25 199 7 src Apr 25 199 7 stamp-h.in May 3 22:54 tar.tar Apr 25 199 7 tests As you can see, the tarball is quite large You can either run gzip on the... in a non-mission critical environment 35 SECURE PROGRAMMING 4172316072 CH35 7/26 /99 2:27 PM Page 686 686 4272316072 CH36 7/26 /99 2:18 PM Page 687 CHAPTER 36 Debugging: GNU gdb by Kurt Wall IN THIS CHAPTER • Compiling for gdb 688 • Using Basic gdb Commands • Advanced gdb Concepts and Commands 698 6 89 4272316072 CH36 7/26 /99 2:18 PM Page 688 688 Special Programming Techniques PART V As much as we all... issue the detach command to allow it to continue executing 703 4272316072 CH36 7/26 /99 2:18 PM Page 704 704 4372316072 part6 7/26 /99 2:15 PM Page 705 Finishing Touches PART VI IN THIS PART • Package Management • Documentation • Licensing 735 721 707 4372316072 part6 7/26 /99 2:15 PM Page 706 CHAPTER 37 4472316072 CH37 7/26 /99 2:26 PM Page 707 Package Management by Kurt Wall IN THIS CHAPTER • Understanding... using discrete values, because gdb can display the values of an arbitrary region of memory To print the first memory locations associated with ary, use the following command: 693 4272316072 CH36 7/26 /99 2:18 PM Page 694 694 Special Programming Techniques PART V This feature may seem rather useless because, of course, you know the types of all the variables in your program (yeah, right!) But, you will change... a running program This is not necessarily a bad thing, but you do need to understand that what you are doing has side effects 36 DEBUGGING: GNU GDB (gdb) print i $14 = 15 695 4272316072 CH36 7/26 /99 2:18 PM Page 696 696 Special Programming Techniques PART V One of the whatis command’s shortcomings is that it only gives you the type of a variable or function If you have a structure, such as the following:... so you can simply press the Enter key to re-execute the last command, a real finger saver This works for most gdb commands See the documentation for more details Table 36.1 697 4272316072 CH36 7/26 /99 2:18 PM Page 698 698 Special Programming Techniques PART V Advanced gdb Concepts and Commands This section discusses a few complicated concepts and some sophisticated commands that will make your gdb usage... section shows you just enough gdb commands to get you going A later section, “Advanced gdb Concepts and Commands,” will cover a few advanced features that will come in handy 6 89 4272316072 CH36 7/26 /99 2:18 PM Page 690 690 Special Programming Techniques PART V I tried to run it by typing /debugme, and it immediately caused a segmentation fault and dumped core (on Red Hat 5.2) The first step is to start... As always, gdb has a complete help system, TeXinfo documentation, and an excellent manual, Debugging With GDB, which is available online and by mail order from the FSF 691 4272316072 CH36 7/26 /99 2:18 PM Page 692 692 Special Programming Techniques PART V the offending line(s) of code exist For this purpose, use the list command, which takes the general form, list [m,n] m and n are the starting and... filename Similarly, given two functions, blat() and splat(), each with an integer variable named idx, the following commands would print the addresses of idx in each function: 699 4272316072 CH36 7/26 /99 2:18 PM Page 700 700 Special Programming Techniques PART V Listing 36.2 30 31 32 33 34 35 CONTINUED } int number(void) { return 10; } Using the break command, set a breakpoint at line 32: (gdb) break 32... files, foo.c and bar.c, each containing a variable named baz, that is declared static, to refer to the one in foo.c you might write the following: (gdb) print ‘foo.c’::baz 4272316072 CH36 7/26 /99 2:18 PM Page 699 Debugging: GNU gdb CHAPTER 36 (gdb) print &blat::idx (gdb) print &splat::idx Traversing the Call Stack provides two commands for moving up and down the call stack, which is the chain of function . but will probably improve service for most. Secure Programming C HAPTER 35 6 79 35 SECURE PROGRAMMING 4172316072 CH35 7/26 /99 2:27 PM Page 6 79 Three Factor Authentication Authentication of users. non-mission critical environment. Secure Programming C HAPTER 35 685 35 SECURE PROGRAMMING 4172316072 CH35 7/26 /99 2:27 PM Page 685 686 4172316072 CH35 7/26 /99 2:27 PM Page 686 IN THIS CHAPTER •. the command $1-1 produces: (gdb) print $1-1 $4 = 381 Special Programming Techniques P ART V 692 4272316072 CH36 7/26 /99 2:18 PM Page 692 You are not limited to using discrete values, because gdb