diff options
author | Sebastian Gerhardt <sebgerhardt@gmx.net> | 2008-10-11 17:55:14 +0000 |
---|---|---|
committer | Sebastian Gerhardt <sebgerhardt@gmx.net> | 2008-10-11 17:55:14 +0000 |
commit | 8636c4b3084d678bf99f2ff0ee46d07727e07a8a (patch) | |
tree | 677c4fd712ad6751d571da5238edf942893b5c28 | |
parent | 8bee920d50ed850e0354b18c6b8cd4a993069620 (diff) | |
download | libmicrohttpd-8636c4b3084d678bf99f2ff0ee46d07727e07a8a.tar.gz libmicrohttpd-8636c4b3084d678bf99f2ff0ee46d07727e07a8a.zip |
Tutorial: New chapter for SSL/TLS
-rw-r--r-- | doc/chapters/tlsauthentication.inc | 130 | ||||
-rw-r--r-- | doc/examples/tlsauthentication.c | 253 | ||||
-rw-r--r-- | doc/tutorial.texi | 11 |
3 files changed, 394 insertions, 0 deletions
diff --git a/doc/chapters/tlsauthentication.inc b/doc/chapters/tlsauthentication.inc new file mode 100644 index 00000000..37b45b8e --- /dev/null +++ b/doc/chapters/tlsauthentication.inc | |||
@@ -0,0 +1,130 @@ | |||
1 | We left the basic authentication chapter with the unsatisfactory conclusion that | ||
2 | any traffic, including the credentials, could be intercepted by anyone between | ||
3 | the browser client and the server. Protecting the data while it is sent over | ||
4 | unsecured lines will be the goal of this chapter. | ||
5 | |||
6 | Since version 0.4, the @emph{MHD} library includes support for encrypting the | ||
7 | traffic by employing SSL/TSL. If @emph{GNU libmicrohttpd} has been configured to | ||
8 | support these, encryption and decryption can be applied transparently on the | ||
9 | data being sent, with only minimal changes to the actual source code of the example. | ||
10 | |||
11 | |||
12 | @heading Preparation | ||
13 | |||
14 | First, a private key for the server will be generated. With this key, the server | ||
15 | will later be able to authenticate itself to the client---preventing anyone else | ||
16 | from stealing the password by faking its identity. The @emph{OpenSSL} suite, which | ||
17 | is available on many operating systems, can generate such a key. For the scope of | ||
18 | this tutorial, we will be content with a 1024 bit key: | ||
19 | @verbatim | ||
20 | > openssl genrsa -out server.key 1024 | ||
21 | @end verbatim | ||
22 | @noindent | ||
23 | |||
24 | In addition to the key, a certificate describing the server in human readable tokens | ||
25 | is also needed. This certificate will be attested with our aforementioned key. In this way, | ||
26 | we obtain a self-signed certificate, valid for one year. | ||
27 | |||
28 | @verbatim | ||
29 | > openssl req -days 365 -out server.pem -new -x509 -key server.key | ||
30 | @end verbatim | ||
31 | @noindent | ||
32 | |||
33 | To avoid unnecessary error messages in the browser, the certificate needs to | ||
34 | have a name that matches the @emph{URI}, for example, "localhost" or the domain. | ||
35 | If you plan to have a publicly reachable server, you will need to ask a trusted third party, | ||
36 | called @emph{Certificate Authority}, or @emph{CA}, to attest the certificate for you. This way, | ||
37 | any visitor can make sure the server's identity is real. | ||
38 | |||
39 | Whether the server's certificate is signed by us or a third party, once it has been accepted | ||
40 | by the client, both sides will be communicating over encrypted channels. From this point on, | ||
41 | it is the client's turn to authenticate itself. But this has already been implemented in the basic | ||
42 | authentication scheme. | ||
43 | |||
44 | |||
45 | @heading Changing the source code | ||
46 | |||
47 | We merely have to extend the server program so that it loads the two files into memory, | ||
48 | |||
49 | @verbatim | ||
50 | int | ||
51 | main () | ||
52 | { | ||
53 | struct MHD_Daemon *daemon; | ||
54 | char *key_pem; | ||
55 | char *cert_pem; | ||
56 | |||
57 | key_pem = load_file (SERVERKEYFILE); | ||
58 | cert_pem = load_file (SERVERCERTFILE); | ||
59 | |||
60 | if ((key_pem == NULL) || (cert_pem == NULL)) | ||
61 | { | ||
62 | printf ("The key/certificate files could not be read.\n"); | ||
63 | return 1; | ||
64 | } | ||
65 | @end verbatim | ||
66 | @noindent | ||
67 | |||
68 | and then we point the @emph{MHD} daemon to it upon initalization. | ||
69 | @verbatim | ||
70 | |||
71 | daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL, NULL, | ||
72 | &answer_to_connection, NULL, | ||
73 | MHD_OPTION_HTTPS_MEM_KEY, key_pem, | ||
74 | MHD_OPTION_HTTPS_MEM_CERT, cert_pem, | ||
75 | MHD_OPTION_END); | ||
76 | |||
77 | if (NULL == daemon) | ||
78 | { | ||
79 | printf ("%s\n", cert_pem); | ||
80 | |||
81 | free (key_pem); | ||
82 | free (cert_pem); | ||
83 | |||
84 | return 1; | ||
85 | } | ||
86 | @end verbatim | ||
87 | @noindent | ||
88 | |||
89 | |||
90 | The rest consists of little new besides some additional memory cleanups. | ||
91 | @verbatim | ||
92 | |||
93 | getchar (); | ||
94 | |||
95 | MHD_stop_daemon (daemon); | ||
96 | free (key_pem); | ||
97 | free (cert_pem); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | @end verbatim | ||
102 | @noindent | ||
103 | |||
104 | |||
105 | The rather unexciting file loader can be found in the complete example @code{tlsauthentication.c}. | ||
106 | |||
107 | @heading Remarks | ||
108 | @itemize @bullet | ||
109 | @item | ||
110 | While the standard @emph{HTTP} port is 80, it is 443 for @emph{HTTPS}. The common internet browsers assume | ||
111 | standard @emph{HTTP} if they are asked to access other ports than these. Therefore, you will have to type | ||
112 | @code{https://localhost:8888} explicitly when you test the example, or the browser will not know how to | ||
113 | handle the answer properly. | ||
114 | |||
115 | @item | ||
116 | The remaining weak point is the question how the server will be trusted initially. Either a @emph{CA} signs the | ||
117 | certificate or the client obtains the key over secure means. Anyway, the clients have to be aware (or configured) | ||
118 | that they should not accept certificates of unknown origin. | ||
119 | |||
120 | @item | ||
121 | The introduced method of certificates makes it mandatory to set an expiration date---making it less feasible to | ||
122 | hardcode certificates in embedded devices. | ||
123 | |||
124 | @item | ||
125 | The cryptographic facilities consume memory space and computing time. For this reason, websites usually consists | ||
126 | both of uncritically @emph{HTTP} parts and secured @emph{HTTPS}. | ||
127 | |||
128 | @end itemize | ||
129 | |||
130 | |||
diff --git a/doc/examples/tlsauthentication.c b/doc/examples/tlsauthentication.c new file mode 100644 index 00000000..437d795a --- /dev/null +++ b/doc/examples/tlsauthentication.c | |||
@@ -0,0 +1,253 @@ | |||
1 | #include <platform.h> | ||
2 | #include <microhttpd.h> | ||
3 | |||
4 | #define PORT 8888 | ||
5 | |||
6 | #define REALM "\"Maintenance\"" | ||
7 | #define USER "a legitimate user" | ||
8 | #define PASSWORD "and his password" | ||
9 | |||
10 | #define SERVERKEYFILE "server.key" | ||
11 | #define SERVERCERTFILE "server.pem" | ||
12 | |||
13 | char *string_to_base64 (const char *message); | ||
14 | |||
15 | long | ||
16 | get_file_size (const char *filename) | ||
17 | { | ||
18 | FILE *fp; | ||
19 | |||
20 | fp = fopen (filename, "rb"); | ||
21 | if (fp) | ||
22 | { | ||
23 | long size; | ||
24 | |||
25 | if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp)))) | ||
26 | size = 0; | ||
27 | |||
28 | fclose (fp); | ||
29 | |||
30 | return size; | ||
31 | } | ||
32 | else | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | char* | ||
37 | load_file (const char* filename) | ||
38 | { | ||
39 | FILE *fp; | ||
40 | char *buffer; | ||
41 | long size; | ||
42 | |||
43 | size = get_file_size (filename); | ||
44 | if (size == 0) return NULL; | ||
45 | |||
46 | fp = fopen (filename, "rb"); | ||
47 | if (!fp) return NULL; | ||
48 | |||
49 | buffer = malloc (size); | ||
50 | if (!buffer) {fclose (fp); return NULL;} | ||
51 | |||
52 | if (size != fread (buffer, 1, size, fp)) | ||
53 | { | ||
54 | free (buffer); | ||
55 | buffer = NULL; | ||
56 | } | ||
57 | |||
58 | fclose (fp); | ||
59 | return buffer; | ||
60 | } | ||
61 | |||
62 | int | ||
63 | ask_for_authentication (struct MHD_Connection *connection, const char *realm) | ||
64 | { | ||
65 | int ret; | ||
66 | struct MHD_Response *response; | ||
67 | char *headervalue; | ||
68 | const char *strbase = "Basic realm="; | ||
69 | |||
70 | response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO); | ||
71 | if (!response) | ||
72 | return MHD_NO; | ||
73 | |||
74 | headervalue = malloc (strlen (strbase) + strlen (realm) + 1); | ||
75 | if (!headervalue) | ||
76 | return MHD_NO; | ||
77 | |||
78 | strcpy (headervalue, strbase); | ||
79 | strcat (headervalue, realm); | ||
80 | |||
81 | ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue); | ||
82 | free (headervalue); | ||
83 | if (!ret) | ||
84 | { | ||
85 | MHD_destroy_response (response); | ||
86 | return MHD_NO; | ||
87 | } | ||
88 | |||
89 | ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); | ||
90 | |||
91 | MHD_destroy_response (response); | ||
92 | |||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | int | ||
97 | is_authenticated (struct MHD_Connection *connection, | ||
98 | const char *username, const char *password) | ||
99 | { | ||
100 | const char *headervalue; | ||
101 | char *expected_b64, *expected; | ||
102 | const char *strbase = "Basic "; | ||
103 | int authenticated; | ||
104 | |||
105 | headervalue = | ||
106 | MHD_lookup_connection_value (connection, MHD_HEADER_KIND, | ||
107 | "Authorization"); | ||
108 | if (NULL == headervalue) | ||
109 | return 0; | ||
110 | if (0 != strncmp (headervalue, strbase, strlen (strbase))) | ||
111 | return 0; | ||
112 | |||
113 | expected = malloc (strlen (username) + 1 + strlen (password) + 1); | ||
114 | if (NULL == expected) | ||
115 | return 0; | ||
116 | |||
117 | strcpy (expected, username); | ||
118 | strcat (expected, ":"); | ||
119 | strcat (expected, password); | ||
120 | |||
121 | expected_b64 = string_to_base64 (expected); | ||
122 | if (NULL == expected_b64) | ||
123 | return 0; | ||
124 | |||
125 | strcpy (expected, strbase); | ||
126 | authenticated = | ||
127 | (strcmp (headervalue + strlen (strbase), expected_b64) == 0); | ||
128 | |||
129 | free (expected_b64); | ||
130 | |||
131 | return authenticated; | ||
132 | } | ||
133 | |||
134 | |||
135 | int | ||
136 | secret_page (struct MHD_Connection *connection) | ||
137 | { | ||
138 | int ret; | ||
139 | struct MHD_Response *response; | ||
140 | const char *page = "<html><body>A secret.</body></html>"; | ||
141 | |||
142 | response = | ||
143 | MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO, | ||
144 | MHD_NO); | ||
145 | if (!response) | ||
146 | return MHD_NO; | ||
147 | |||
148 | ret = MHD_queue_response (connection, MHD_HTTP_OK, response); | ||
149 | MHD_destroy_response (response); | ||
150 | |||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | |||
155 | int | ||
156 | answer_to_connection (void *cls, struct MHD_Connection *connection, | ||
157 | const char *url, const char *method, | ||
158 | const char *version, const char *upload_data, | ||
159 | unsigned int *upload_data_size, void **con_cls) | ||
160 | { | ||
161 | if (0 != strcmp (method, "GET")) | ||
162 | return MHD_NO; | ||
163 | if (NULL == *con_cls) | ||
164 | { | ||
165 | *con_cls = connection; | ||
166 | return MHD_YES; | ||
167 | } | ||
168 | |||
169 | if (!is_authenticated (connection, USER, PASSWORD)) | ||
170 | return ask_for_authentication (connection, REALM); | ||
171 | |||
172 | return secret_page (connection); | ||
173 | } | ||
174 | |||
175 | |||
176 | int | ||
177 | main () | ||
178 | { | ||
179 | struct MHD_Daemon *daemon; | ||
180 | char *key_pem; | ||
181 | char *cert_pem; | ||
182 | |||
183 | key_pem = load_file (SERVERKEYFILE); | ||
184 | cert_pem = load_file (SERVERCERTFILE); | ||
185 | |||
186 | if ((key_pem == NULL) || (cert_pem == NULL)) | ||
187 | { | ||
188 | printf ("The key/certificate files could not be read.\n"); | ||
189 | return 1; | ||
190 | } | ||
191 | |||
192 | daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL, NULL, | ||
193 | &answer_to_connection, NULL, | ||
194 | MHD_OPTION_HTTPS_MEM_KEY, key_pem, MHD_OPTION_HTTPS_MEM_CERT, cert_pem, | ||
195 | MHD_OPTION_END); | ||
196 | if (NULL == daemon) | ||
197 | { | ||
198 | printf ("%s\n", cert_pem); | ||
199 | |||
200 | free (key_pem); | ||
201 | free (cert_pem); | ||
202 | |||
203 | return 1; | ||
204 | } | ||
205 | |||
206 | getchar (); | ||
207 | |||
208 | MHD_stop_daemon (daemon); | ||
209 | free (key_pem); | ||
210 | free (cert_pem); | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | |||
216 | char * | ||
217 | string_to_base64 (const char *message) | ||
218 | { | ||
219 | const char *lookup = | ||
220 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
221 | unsigned long l; | ||
222 | int i; | ||
223 | char *tmp; | ||
224 | size_t length = strlen (message); | ||
225 | |||
226 | tmp = malloc (length * 2); | ||
227 | if (NULL == tmp) | ||
228 | return tmp; | ||
229 | |||
230 | tmp[0] = 0; | ||
231 | |||
232 | for (i = 0; i < length; i += 3) | ||
233 | { | ||
234 | l = (((unsigned long) message[i]) << 16) | ||
235 | | (((i + 1) < length) ? (((unsigned long) message[i + 1]) << 8) : 0) | ||
236 | | (((i + 2) < length) ? ((unsigned long) message[i + 2]) : 0); | ||
237 | |||
238 | |||
239 | strncat (tmp, &lookup[(l >> 18) & 0x3F], 1); | ||
240 | strncat (tmp, &lookup[(l >> 12) & 0x3F], 1); | ||
241 | |||
242 | if (i + 1 < length) | ||
243 | strncat (tmp, &lookup[(l >> 6) & 0x3F], 1); | ||
244 | if (i + 2 < length) | ||
245 | strncat (tmp, &lookup[l & 0x3F], 1); | ||
246 | } | ||
247 | |||
248 | if (length % 3) | ||
249 | strncat (tmp, "===", 3 - length % 3); | ||
250 | |||
251 | return tmp; | ||
252 | } | ||
253 | |||
diff --git a/doc/tutorial.texi b/doc/tutorial.texi index bc6ebe54..7b9b877e 100644 --- a/doc/tutorial.texi +++ b/doc/tutorial.texi | |||
@@ -39,6 +39,7 @@ Free Documentation License". | |||
39 | * Supporting basic authentication:: | 39 | * Supporting basic authentication:: |
40 | * Processing POST data:: | 40 | * Processing POST data:: |
41 | * Improved processing of POST data:: | 41 | * Improved processing of POST data:: |
42 | * Adding a layer of security:: | ||
42 | * Bibliography:: | 43 | * Bibliography:: |
43 | * License text:: | 44 | * License text:: |
44 | * Example programs:: | 45 | * Example programs:: |
@@ -72,6 +73,10 @@ Free Documentation License". | |||
72 | @chapter Improved processing of POST data | 73 | @chapter Improved processing of POST data |
73 | @include chapters/largerpost.inc | 74 | @include chapters/largerpost.inc |
74 | 75 | ||
76 | @node Adding a layer of security | ||
77 | @chapter Adding a layer of security | ||
78 | @include chapters/tlsauthentication.inc | ||
79 | |||
75 | @node Bibliography | 80 | @node Bibliography |
76 | @appendix Bibliography | 81 | @appendix Bibliography |
77 | @include chapters/bibliography.inc | 82 | @include chapters/bibliography.inc |
@@ -89,6 +94,7 @@ Free Documentation License". | |||
89 | * basicauthentication.c:: | 94 | * basicauthentication.c:: |
90 | * simplepost.c:: | 95 | * simplepost.c:: |
91 | * largepost.c:: | 96 | * largepost.c:: |
97 | * tlsauthentication.c:: | ||
92 | @end menu | 98 | @end menu |
93 | 99 | ||
94 | @node hellobrowser.c | 100 | @node hellobrowser.c |
@@ -127,5 +133,10 @@ Free Documentation License". | |||
127 | @verbatiminclude examples/largepost.c | 133 | @verbatiminclude examples/largepost.c |
128 | @end smalldisplay | 134 | @end smalldisplay |
129 | 135 | ||
136 | @node tlsauthentication.c | ||
137 | @section tlsauthentication.c | ||
138 | @smalldisplay | ||
139 | @verbatiminclude examples/tlsauthentication.c | ||
140 | @end smalldisplay | ||
130 | 141 | ||
131 | @bye | 142 | @bye |