<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta http-equiv="Content-Style-Type" content="text/css">
<title>The world of TOMOYO Linux&nbsp;&nbsp;The seventh installment: "Let's try various functionalities."</title>
<link rel="stylesheet" href="http://tomoyo.sourceforge.jp/tomoyo.css" media="all" type="text/css">
</head>
<body>
<p style="text-align:right;"><a href="tutorial-7.html.ja">Japanese Page</a></p>
<p style="text-align:right;">Last modified: $Date: 2011-07-01 13:55:23 +0900 (Fri, 01 Jul 2011) $</p>

<h1>The world of TOMOYO Linux<br>The seventh installment: "Let's try various functionalities."</h1>

<h2>Contents of this installment.</h2>

<p>By now in this series, I explained access control on files. Access control on files prevents accessing unnecessary programs and data files and plays basic role for improving security using secure OS. But access control on files alone is not sufficient. In this installment, I explain major access controls which TOMOYO Linux provides, mainly access control on networks.</p>

<h2>About access control on networks</h2>

<p>By default, Linux provides iptables for doing packet filtering. TOMOYO Linux provides similar functionality, but TOMOYO Linux's access control on networks can perform packet filtering for per TOMOYO Linux's domain and per user ID basis, and easy to configure. You can use TOMOYO Linux's access control on networks even if iptables is too difficult for you to configure. (But note that there are functionalities which iptables can provide but TOMOYO Linux can't provide, for TOMOYO Linux is independent with iptables.)</p>

<h3>Coverage of access control on networks</h3>

<p>Coverage of TOMOYO Linux's access control on networks is shown in Fig. 1.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 1&nbsp;&nbsp;Coverage of access control on networks
<pre>
(Regarding Internet domain socket)
Stream (a.k.a. TCP) protocol / Datagram (a.k.a. UDP) protocol / Raw (a.k.a. IP) protocol
IPv4 network address / IPv6 network address
Port number (for stream and datagram) / Protocol number (for raw)
</pre>
<pre>
(Regarding Unix domain socket)
Stream protocol / Datagram protocol / SeqPacket protocol
Unix domain address on filesystem / Unix domain address in abstract name space
</pre>
</td></tr>
</table>

<p>By using TOMOYO Linux's access control on networks, you can allow, for example, svn command which is executed by a user whose user ID is 500 and who logged in via /usr/sbin/sshd can communicate with port 80 on host 202.221.179.21 using TCP protocol. I explain steps to configure in following sections.</p>

<h3>Preparation</h3>

<p>You can specify access control on networks for per a domain basis like access control on files. For this installment, I create 3 profiles for "a profile for learning mode for network only" "a profile for permissive mode for network only" "a profile for enforcing mode for network only".</p>

<h4>(1) Creating profiles</h4>

<p>As a preparation for access control on network, create profile 5 to 7. Append lines listed in Fig. 2 to the bottom of /etc/ccs/profile.conf .</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 2&nbsp;&nbsp;Lines to be appended to /etc/ccs/profile.conf
<pre>
5-COMMENT=Learning Mode (Network Only)
5-CONFIG::network={ mode=learning }
6-COMMENT=Permissive Mode (Network Only)
6-CONFIG::network={ mode=permissive }
7-COMMENT=Enforcing Mode (Network Only)
7-CONFIG::network={ mode=enforcing }
</pre>
</td></tr>
</table>

<p>Save the file and then reflect the changes by doing command in Fig. 3.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 3&nbsp;&nbsp;Reflect the changes
<pre>
# /usr/sbin/ccs-loadpolicy -p &lt; /etc/ccs/profile.conf
</pre>
</td></tr>
</table>

<h4>(2) Determining target domains</h4>

<p>Target domains for applying access control on network are arbitrary. In this installment, I choose domains under currently operating shell sessions as target domains. If you are usingh /bin/bash , you can see the domain which /bin/bash process belongs to by doing commands in Fig. 4.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 4&nbsp;&nbsp;Assign the domainname of currently executing /bin/bash process to environment variable SELF_DOMAIN.
<pre>
# exec 1023&lt;/proc/ccs/self_domain
# read -u 1023 SELF_DOMAIN
# echo "$SELF_DOMAIN"
&lt;kernel&gt; /usr/sbin/sshd /bin/bash
</pre>
</td></tr>
</table>

<p>If the domain which currently operating shell sessions belong is specified using "keep_domain" keyword, delete the "keep_domain" keyword for this section using policy editor. (This is just for making it easier for you to understand. You don't need to delete "keep_domain" keyword when you apply on actual environment.) Note that the IP addresses and port numbers learned may differ depending on your environment.</p>

<h3>Let TOMOYO Linux learn the permissions</h3>

<h4>(1) Examples for client applications</h4>

<p>Change a domain which current process belongs to and its descendant domains to learning mode by doing commands listed in Fig. 5.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 5&nbsp;&nbsp;Change a domain which current process belongs to and its descendant domains to learning mode
<pre>
# /usr/sbin/ccs-setprofile -r 5 "$SELF_DOMAIN"
</pre>
</td></tr>
</table>

<p>As an example command that uses networks, let's access http://www.sourceforge.jp/ using /usr/bin/curl command. (Fig. 6)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 6&nbsp;&nbsp;Execute /usr/bin/curl command
<pre>
# curl http://www.sourceforge.jp/ &gt; /dev/null
</pre>
</td></tr>
</table>

<p>If /usr/bin/curl command is not available, you can use other commands like /usr/bin/wget command or /usr/kerberos/bin/telnet command. Run the policy editor and browse permissions for the domain for /usr/bin/curl command. (Fig. 7)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 7&nbsp;&nbsp;Policy configuration for a domain for /usr/bin/curl command<br>
<img src="tutorial/fig-7-7.png" alt="fig-7-7.png" width="556" height="210">
</td></tr>
</table>

<p>Lines starting with "network" keyword are permissions for access control on networks. "network inet dgram send 192.168.1.2 53" and "network inet dgram recv 192.168.1.2 53" allow communicating with port 53 (i.e. DNS service) on host 192.168.1.2 using UDP protocol. Also, "network inet stream connect 202.221.179.21 80" allows communicating with port 80 (i.e. HTTP service) on host 202.221.179.21 (this is IP address for www.sourceforge.jp as of this writing) using TCP protocol.</p>

<h4>(2) Examples for server applications</h4>

<p>In this section, let's login to SSH service using /usr/bin/ssh command. (If you don't have SSH service, you can use other services like HTTP.) If "initialize_domain /usr/sbin/sshd" is specified in the exception policy (it should be specified by init_policy command which I executed in the first installment of this series), /usr/sbin/sshd runs in the "&lt;kernel&gt; /usr/sbin/sshd" domain. Thus, assign a profile for learning mode to the "&lt;kernel&gt; /usr/sbin/sshd" domain. (Fig. 8)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 8&nbsp;&nbsp;Assign a profile for learning mode to "&lt;kernel&gt; /usr/sbin/sshd" domain
<pre>
# /usr/sbin/ccs-setprofile 5 '&lt;kernel&gt; /usr/sbin/sshd'
</pre>
</td></tr>
</table>

<p>Then, login to localhost using ssh command (Fig. 9) and run the policy editor and browse permissions for "&lt;kernel&gt; /usr/sbin/sshd" domain. (Fig. 10)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 9&nbsp;&nbsp;Login to localhost using ssh command
<pre>
# ssh localhost
</pre>
</td></tr>
</table>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 10&nbsp;&nbsp;Policy configuration for "&lt;kernel&gt; /usr/sbin/sshd" domain<br>
<img src="tutorial/fig-7-10.png" alt="fig-7-10.png" width="556" height="274">
</td></tr>
</table>

<p>"network inet stream accept 0:0:0:0:0:ffff:7f00:1 39044" allows accepting TCP connections from port 39044 on host (IPv6 address) 0:0:0:0:0:ffff:7f00:1.</p>

<p>You can apply conditions to permissions for access control on networks as well as permissions for access control on files. For example, you can restrict remote server's IP addresses based on user ID by using process's user ID as conditions. (Fig. 11)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 11&nbsp;&nbsp;Restricting remote servers based on user ID
<pre>
network inet stream connect 202.221.179.21 80 task.uid=500
network inet stream connect 66.35.250.203 80 task.uid=501
</pre>
</td></tr>
</table>

<p>Regarding IP sockets, you specify protocol number instead of port number. I explain using ping command as an example. Execute command in Fig. 12 and then execute the policy editor and browse permissions for the domain for ping command. (Fig. 13)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 12&nbsp;&nbsp;Executing ping command
<pre>
# ping -c 4 www.sourceforge.jp
</pre>
</td></tr>
</table>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 13&nbsp;&nbsp;Policy configuration for the domain for ping command<br>
<img src="tutorial/fig-7-13.png" alt="fig-7-13.png" width="508" height="226">
</td></tr>
</table>

<p>You can see a line "network inet raw send 202.221.179.21 1" and a line "network inet raw recv 202.221.179.21 1". Fig. 12 shows that the domain which ping command is running is allowed to communicate with host 202.21.179.21 using IP sockets with protocol number 1 (i.e. ICOMP).</p>

<h3>Let's tune policy</h3>

<p>After you let TOMOYO Linux learn permissions, verify the permissions using permissive mode. Assign profile 6 to change to permissive mode. (Fig. 14)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 14&nbsp;&nbsp;Change to permissive mode by assigning profile 6
<pre>
# /usr/sbin/ccs-setprofile -r 6 "$SELF_DOMAIN"
</pre>
</td></tr>
</table>

<h4>(1) Patternizing IP addresses</h4>

<p>You need to patternize IP address ranges for doing access control on networks as well as you patternized file's pathnames for doing access control on files. To patternize IP addresses, specify address ranges using "StartAddress-EndAddress" format like Fig. 15. No space is permitted around "-".</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 15&nbsp;&nbsp;Specifying IPv4 address ranges
<pre>
xxx.xxx.xxx.xxx-yyy.yyy.yyy.yyy
</pre>
</td></tr>
</table>

<p>You can specify IPv6 address as well as IPv4 address. But "::" and "." are not supported for IPv6 address. You need to specify IPv6 address using format like Fig. 16. Also, be sure to specify all possible IP addresses if target servers has multiple IP addresses (e.g. load balancing).</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 16&nbsp;&nbsp;Specifying IPv6 address
<pre>
xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
</pre>
</td></tr>
</table>

<h4>(2) Patternizing port numbers</h4>

<p>Generally, the port number which servers use for accepting connections from  clients are fixed, but the port number which clients use for establishing connections with servers are not fixed. Therefore, you need to specify possible port number ranges which clients might use when giving permissions to server side programs. To patternize, specify port number ranges using "StartPort-Endport" format like Fig. 17. No space is permitted around "-". Three patterns listed in Fig. 18 are port number ranges frequently used.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 17&nbsp;&nbsp;Specifying port number ranges
<pre>
xxxxx-yyyyy
</pre>
</td></tr>
</table>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 18&nbsp;&nbsp;Frequently used port number ranges
<pre>
1024-65535
32768-61000
600-1023
</pre>
</td></tr>
</table>

<p>The first pattern is for port numbers which normal client programs choose when establishing connections with server programs, and you likely specify this range for server programs. The second pattern is for port numbers which are specified in /proc/sys/net/ipv4/ip_local_port_range . Normal client programs choose an unused port from this range when establishing connections with server programs, and you likely specify this range for client programs. Check /proc/sys/net/ipv4/ip_local_port_range when determining the second pattern.</p>

<p>The last pattern is for port numbers used by programs like portmap command which require privileged port numbers (port numbers smaller than 1024) for communication. The actual port numbers required by such programs may be different. The easy way for determining whether patternizing port numbers are necessary or not is to execute programs for several times. For example, permissions with contiguous port numbers like Fig. 19 will be appended by learning mode. Thus, guess required port number ranges from the result of learning mode.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 19&nbsp;&nbsp;Example of permissions with contiguous port numbers
<pre>
network inet stream bind 127.0.0.1 610
network inet stream bind 127.0.0.1 611
network inet stream bind 127.0.0.1 612
network inet stream bind 127.0.0.1 613
</pre>
</td></tr>
</table>

<p>If you find permissions in Fig. 19, you likely need permissions in Fig. 20.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 20&nbsp;&nbsp;Patternized permissions based on Fig. 19
<pre>
network inet stream bind 127.0.0.1 600-1023
</pre>
</td></tr>
</table>

<p>If you find permissions in Fig. 21, you likely need permissions in Fig. 22 or Fig. 23.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 21&nbsp;&nbsp;Example of permissions with contiguous port numbers
<pre>
network inet stream accept 127.0.0.1 32780
network inet stream accept 127.0.0.1 32781
network inet stream accept 127.0.0.1 32782
network inet stream accept 127.0.0.1 32783
network inet stream accept 127.0.0.1 32784
</pre>
</td></tr>
</table>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 22&nbsp;&nbsp;Patternized permissions based on Fig. 21
<pre>
network inet stream accept 127.0.0.1 1024-65535
</pre>
</td></tr>
</table>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 23&nbsp;&nbsp;Patternized permissions based on Fig. 21
<pre>
network inet stream accept 127.0.0.1 32768-61000
</pre>
</td></tr>
</table>

<h3>Let's enforce access control on networks</h3>

<p>When you became sure that you gave enough permission, let's enforce access control on networks. Assign profile 7 to change to enforcing mode. (Fig. 24)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 24&nbsp;&nbsp;Change to enforcing mode by assigning profile 7
<pre>
# /usr/sbin/ccs-setprofile -r 7 "$SELF_DOMAIN"
</pre>
</td></tr>
</table>

<p>Note that profile 5 to 7 does not perform access control on files. By using ccs-queryd which I explained in the fourth installment of this series, you can interactively append missing permissions. I think you understood that you can break access control on networks provided by iptables by using TOMOYO Linux.</p>

<h2>About access control on capabilities</h2>

<p>Linux kernel 2.2 and later supports POSIX capabilities. Capabilities realize secure OS's basic concept "least privileges" and are used for restricting use of various privileged operations for per a process basis. You can find list of capabilities which Linux supports in /usr/include/linux/capability.h . However, since CAP_SYS_ADMIN privilege is used for various purposes, we cannot expect fine grained restriction. Therefore, TOMOYO Linux defined and uses original capabilities. (Hereafter, capabilities refers to TOMOYO Linux's capabilities rather than POSIX's capabilities.)</p>

<p>While TOMOYO Linux uses original capabilities that are not compatible with POSIX capabilities, TOMOYO Linux's capabilities are not visible from userland applications. Thus, you don't need to modify userland programs. TOMOYO Linux 1.8.0 uses capabilities listed in Fig. 25.</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 25&nbsp;&nbsp;Capabilities defined in TOMOYO Linux 1.8.0<br>
<table border="1" summary="fig">
<tr><td>Type</td><td>Meaning</td></tr>
<tr><td>use_route</td><td>Permit use of ROUTE sockets.</td></tr>
<tr><td>use_packet</td><td>Permit use of PACKET sockets.</td></tr>
<tr><td>use_kernel_module</td><td>Permit use of create_module(2) init_module(2) delete_module(2) syscall.</td></tr>
<tr><td>SYS_REBOOT</td><td>Permit use of reboot(2) syscall.</td></tr>
<tr><td>SYS_VHANGUP</td><td>Permit use of vhangup(2) syscall.</td></tr>
<tr><td>SYS_TIME</td><td>Permit use of stime(2) settimeofday(2) adjtimex(2) syscall.</td></tr>
<tr><td>SYS_NICE</td><td>Permit use of nice(2) setpriority(2) syscall.</td></tr>
<tr><td>SYS_SETHOSTNAME</td><td>Permit use of sethostname(2) setdomainname(2) syscall.</td></tr>
<tr><td>SYS_KEXEC_LOAD</td><td>Permit use of kexec_load(2) syscall.</td></tr>
<tr><td>SYS_PTRACE</td><td>Permit use of ptrace(2) syscall.</td></tr>
</table>
</td></tr>
</table>

<p>In this section, I explain about privilege for changing system clock (i.e. SYS_TIME ) as an example. Steps for doing access control on capabilities are the same with doing access control on networks. Firstly, specify capabilities you want to control in the profiles. Append lines listed in Fig. 26 to the bottom of /etc/ccs/profile.conf .</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 26&nbsp;&nbsp;Lines to be appended to /etc/ccs/profile.conf
<pre>
9-CONFIG::capability::SYS_TIME={ mode=learning }
10-CONFIG::capability::SYS_TIME={ mode=enforcing }
</pre>
</td></tr>
</table>

<p>Unlike files and networks, capabilities do not have operands. All necessary capabilities are given by simply using learning mode. Thus, a profile for permissive mode is omitted in Fig. 26. Save the file and then reflect the changes by doing command in Fig. 3.</p>

<p>For explanation, I firstly use a profile for enforcing mode. (Fig. 27)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 27&nbsp;&nbsp;Assign a profile for enforcing mode
<pre>
# /usr/sbin/ccs-setprofile -r 10 "$SELF_DOMAIN"
</pre>
</td></tr>
</table>

<p>Then, change system clock. (Fig. 28)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 28&nbsp;&nbsp;Change system clock
<pre>
# date 112200002010.00
date: cannot set date: Operation not permitted
Mon Nov 22 00:00:00 JST 2010
</pre>
</td></tr>
</table>

<p>The operation was rejected. This is because the domain which date command belongs to was not given a capability for changing system clock. Next, assign a profile for learning mode. (Fig. 29)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 29&nbsp;&nbsp;Assign a profile for learning mode
<pre>
# /usr/sbin/ccs-setprofile -r 9 "$SELF_DOMAIN"
</pre>
</td></tr>
</table>

<p>Again, change system clock. (Fig. 30)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 30&nbsp;&nbsp;Change system clock
<pre>
# date 112200002010.00
Mon Nov 22 00:00:00 JST 2010
</pre>
</td></tr>
</table>

<p>This time, the operation succeeded. Run the policy editor and find the domain for date command and browse permissions for that domain. (Fig. 31)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 31&nbsp;&nbsp;Permission "capability SYS_TIME" was learned<br>
<img src="tutorial/fig-7-31.png" alt="fig-7-31.png" width="508" height="146">
</td></tr>
</table>

<p>You can find that a permission "capability SYS_TIME" was learned. Now, you can forbid changing system clock from domains without SYS_TIME capability by assigning a profile for enforcing mode.</p>

<h2>About access control on signals</h2>

<p>TOMOYO Linux can do access control on signal numbers and signal targets. Steps for doing access control on signals are the same with doing access control on networks. Append lines listed in Fig. 32 to the bottom of /etc/ccs/profile.conf .</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 32&nbsp;&nbsp;Lines to be appended to /etc/ccs/profile.conf
<pre>
11-CONFIG::ipc::signal={ mode=learning }
12-CONFIG::ipc::signal={ mode=enforcing }
</pre>
</td></tr>
</table>

<p>Save the file and then reflect the changes by doing command in Fig. 3.</p>

<p>I assume below operations are done from "&lt;kernel&gt; /usr/sbin/sshd /bin/bash" domain (i.e. login shell executed by /usr/sbin/sshd ). Make sure that "keep_domain &lt;kernel&gt; /usr/sbin/sshd /bin/bash" is not specified in the exception policy. Firstly, execute /bin/sleep command. (Fig. 33)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 33&nbsp;&nbsp;Execute /bin/sleep
<pre>
# sleep 100
</pre>
</td></tr>
</table>

<p>At this state, open another window and login to the system. For explanation, I firstly use a profile for enforcing mode. (Fig. 34)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 34&nbsp;&nbsp;Assign a profile for enforcing mode
<pre>
# /usr/sbin/ccs-setprofile -r 12 "$SELF_DOMAIN"
</pre>
</td></tr>
</table>

<p>Then, get the process ID of running /bin/sleep command and send a signal to that process ID. (Fig. 35. Use external /bin/kill command rather than shell's built-in kill command.)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 35&nbsp;&nbsp;Send signal to process ID of /bin/sleep command
<pre>
# pidof sleep
4649
# /bin/kill -15 4649
kill 4649: Operation not permitted
</pre>
</td></tr>
</table>

<p>The kill operation was rejected. This is because the domain which /bin/kill command belongs to was not given permission for sending signals. Next, assign a profile for learning mode (Fig. 36) and resend the signal. (Fig. 37)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 36&nbsp;&nbsp;Assign a profile for learning mode
<pre>
# /usr/sbin/ccs-setprofile -r 11 "$SELF_DOMAIN"
</pre>
</td></tr>
</table>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 37&nbsp;&nbsp;Send signal to process ID of /bin/sleep command again
<pre>
# /bin/kill -15 4649
</pre>
</td></tr>
</table>

<p>This time, signal operation succeeded. Run the policy editor and find the domain for kill command and browse permissions for that domain. (Fig. 38)</p>

<table border="1" summary="fig">
<tr><td>
&diams; Fig. 38&nbsp;&nbsp;Permissions given to the domain for kill command<br>
<img src="tutorial/fig-7-38.png" alt="fig-7-38.png" width="556" height="146">
</td></tr>
</table>

<p>You can find a permission "ipc signal 15 &lt;kernel&gt; /usr/sbin/sshd /bin/bash /bin/sleep" was learned. This permission allows sending signal 15 (i.e. SIGTERM) to "&lt;kernel&gt; /usr/sbin/sshd /bin/bash /bin/sleep" domain and its descendant domains. Since TOMOYO Linux's domains forms hierarchal structure starting with "&lt;kernel&gt;", "ipc signal 15 &lt;kernel&gt;" will allow sending signal 15 to any processes. Also, as exceptions, sending signals to the same domain (e.g. threads in current process) is always permitted, and sending signal 0 is always permitted.</p>

<h2>Trailer</h2>

<p>In this installment, I explained mainly access control on networks. I think you understood that you can restrict domains that can use networks and apply powerful and flexible access controls using TOMOYO Linux. In the next installment, I explain steps for restricting login sessions. Don't miss it!</p>

<p><a href="tutorial-6.html.en">Go back to the sixth installment.</a>&nbsp;&nbsp;<a href="tutorial-8.html.en">Proceed to the eighth installment.</a></p>

<hr>

<p><a href="index.html.en#tutorial">Return to index page.</a></p>
<p><a href="http://sourceforge.jp/"><img src="http://sourceforge.jp/sflogo.php?group_id=1973" width="96" height="31" alt="sflogo.php" title="SourceForge.jp"></a></p>
</body>
</html>
