1 /* SMB protocol implementation */
8 #ifdef HAVE_LIBSMBCLIENT_H
9 #include <libsmbclient.h>
15 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
23 #include "cache/cache.h"
24 #include "config/options.h"
25 #include "intl/gettext/libintl.h"
26 #include "main/module.h"
27 #include "main/select.h"
28 #include "network/connection.h"
29 #include "network/socket.h"
30 #include "osdep/osdep.h"
31 #include "protocol/auth/auth.h"
32 #include "protocol/common.h"
33 #include "protocol/protocol.h"
34 #include "protocol/smb/smb.h"
35 #include "protocol/uri.h"
36 #include "util/conv.h"
37 #include "util/file.h"
38 #include "util/memory.h"
39 #include "util/string.h"
41 struct module smb_protocol_module
= struct_module(
42 /* name: */ N_("SMB"),
45 /* submodules: */ NULL
,
51 static FILE *header_out
, *data_out
;
53 /* The child process generally does not bother to free the memory it
54 * allocates. When the process exits, the operating system will free
55 * the memory anyway. There is no point in changing this, because the
56 * child process also inherits memory allocations from the parent
57 * process, and it would be very cumbersome to free those. */
60 smb_error(struct connection_state error
)
62 if (is_system_error(error
))
63 fprintf(data_out
, "S%d\n", (int) error
.syserr
);
65 fprintf(data_out
, "I%d\n", (int) error
.basic
);
66 fputs("text/x-error", header_out
);
70 /** First information such as permissions is gathered for each directory entry.
71 * All entries are then sorted. */
72 static struct directory_entry
*
73 get_smb_directory_entries(int dir
, struct string
*prefix
)
75 struct directory_entry
*entries
= NULL
;
78 struct smbc_dirent
*entry
;
80 while ((entry
= smbc_readdir(dir
))) {
82 struct directory_entry
*new_entries
;
86 if (!strcmp(entry
->name
, "."))
89 new_entries
= mem_realloc(entries
, (size
+ 2) * sizeof(*new_entries
));
90 if (!new_entries
) continue;
91 entries
= new_entries
;
93 if (!init_string(&attrib
)) {
97 if (!init_string(&name
)) {
102 add_string_to_string(&name
, prefix
);
103 add_to_string(&name
, entry
->name
);
105 stp
= (smbc_stat(name
.source
, &st
)) ? NULL
: &st
;
107 stat_type(&attrib
, stp
);
108 stat_mode(&attrib
, stp
);
109 stat_links(&attrib
, stp
);
110 stat_user(&attrib
, stp
);
111 stat_group(&attrib
, stp
);
112 stat_size(&attrib
, stp
);
113 stat_date(&attrib
, stp
);
115 entries
[size
].name
= stracpy(entry
->name
);
116 entries
[size
].attrib
= attrib
.source
;
123 /* We may have allocated space for entries but added none. */
124 mem_free_if(entries
);
127 qsort(entries
, size
, sizeof(*entries
), compare_dir_entries
);
128 memset(&entries
[size
], 0, sizeof(*entries
));
133 add_smb_dir_entry(struct directory_entry
*entry
, struct string
*page
,
134 int pathlen
, unsigned char *dircolor
)
136 unsigned char *lnk
= NULL
;
137 struct string html_encoded_name
;
138 struct string uri_encoded_name
;
140 if (!init_string(&html_encoded_name
)) return;
141 if (!init_string(&uri_encoded_name
)) {
142 done_string(&html_encoded_name
);
146 encode_uri_string(&uri_encoded_name
, entry
->name
+ pathlen
, -1, 1);
147 add_html_to_string(&html_encoded_name
, entry
->name
+ pathlen
,
148 strlen(entry
->name
) - pathlen
);
150 /* add_to_string(&fragment, &fragmentlen, " "); */
151 add_html_to_string(page
, entry
->attrib
, strlen(entry
->attrib
));
152 add_to_string(page
, "<a href=\"");
153 add_string_to_string(page
, &uri_encoded_name
);
155 if (entry
->attrib
[0] == 'd') {
156 add_char_to_string(page
, '/');
158 #ifdef FS_UNIX_SOFTLINKS
159 } else if (entry
->attrib
[0] == 'l') {
161 unsigned char buf
[MAX_STR_LEN
];
162 int readlen
= readlink(entry
->name
, buf
, MAX_STR_LEN
);
164 if (readlen
> 0 && readlen
!= MAX_STR_LEN
) {
166 lnk
= straconcat(" -> ", buf
, (unsigned char *) NULL
);
169 if (!stat(entry
->name
, &st
) && S_ISDIR(st
.st_mode
))
170 add_char_to_string(page
, '/');
174 add_to_string(page
, "\">");
176 if (entry
->attrib
[0] == 'd' && *dircolor
) {
177 /* The <b> is for the case when use_document_colors is off. */
178 string_concat(page
, "<font color=\"", dircolor
, "\"><b>",
179 (unsigned char *) NULL
);
182 add_string_to_string(page
, &html_encoded_name
);
183 done_string(&uri_encoded_name
);
184 done_string(&html_encoded_name
);
186 if (entry
->attrib
[0] == 'd' && *dircolor
) {
187 add_to_string(page
, "</b></font>");
190 add_to_string(page
, "</a>");
192 add_html_to_string(page
, lnk
, strlen(lnk
));
196 add_char_to_string(page
, '\n');
199 /* First information such as permissions is gathered for each directory entry.
200 * Finally the sorted entries are added to the @data->fragment one by one. */
202 add_smb_dir_entries(struct directory_entry
*entries
, unsigned char *dirpath
,
205 unsigned char dircolor
[8];
208 /* Setup @dircolor so it's easy to check if we should color dirs. */
209 if (get_opt_bool("document.browse.links.color_dirs", NULL
)) {
210 color_to_string(get_opt_color("document.colors.dirs", NULL
),
211 (unsigned char *) &dircolor
);
216 for (i
= 0; entries
[i
].name
; i
++) {
217 add_smb_dir_entry(&entries
[i
], page
, 0, dircolor
);
218 mem_free(entries
[i
].attrib
);
219 mem_free(entries
[i
].name
);
222 /* We may have allocated space for entries but added none. */
223 mem_free_if(entries
);
227 smb_directory(int dir
, struct string
*prefix
, struct uri
*uri
)
230 struct directory_entry
*entries
;
232 if (!is_in_state(init_directory_listing(&buf
, uri
), S_OK
)) {
233 smb_error(connection_state(S_OUT_OF_MEM
));
236 fputs("text/html", header_out
);
239 entries
= get_smb_directory_entries(dir
, prefix
);
240 add_smb_dir_entries(entries
, NULL
, &buf
);
241 add_to_string(&buf
, "</pre><hr/></body></html>\n");
243 fputs(buf
.source
, data_out
);
249 smb_auth(const char *srv
, const char *shr
, char *wg
, int wglen
, char *un
,
250 int unlen
, char *pw
, int pwlen
)
255 #define READ_SIZE 4096
258 do_smb(struct connection
*conn
)
260 struct uri
*uri
= conn
->uri
;
261 struct auth_entry
*auth
= find_auth(uri
);
262 struct string string
;
266 if ((uri
->userlen
&& uri
->passwordlen
) || !auth
) {
267 url
= get_uri_string(uri
, URI_BASE
);
269 unsigned char *uri_string
= get_uri_string(uri
, URI_HOST
| URI_PORT
| URI_DATA
);
271 if (!uri_string
|| !init_string(&string
)) {
272 smb_error(connection_state(S_OUT_OF_MEM
));
274 /* Must URI-encode the username and password to avoid
275 * ambiguity if they contain "/:@" characters.
276 * Libsmbclient then decodes them again, and the
277 * server gets them as they were in auth->user and
278 * auth->password, i.e. as the user typed them in the
279 * auth dialog. This implies that, if the username or
280 * password contains some characters or bytes that the
281 * user cannot directly type, then she cannot enter
282 * them. If that becomes an actual problem, it should
283 * be fixed in the auth dialog, e.g. by providing a
284 * hexadecimal input mode. */
285 add_to_string(&string
, "smb://");
286 encode_uri_string(&string
, auth
->user
, -1, 1);
287 add_char_to_string(&string
, ':');
288 encode_uri_string(&string
, auth
->password
, -1, 1);
289 add_char_to_string(&string
, '@');
290 add_to_string(&string
, uri_string
);
295 smb_error(connection_state(S_OUT_OF_MEM
));
297 if (smbc_init(smb_auth
, 0)) {
298 smb_error(connection_state_for_errno(errno
));
302 dir
= smbc_opendir(url
);
304 struct string prefix
;
306 init_string(&prefix
);
307 add_to_string(&prefix
, url
);
308 add_char_to_string(&prefix
, '/');
309 smb_directory(dir
, &prefix
, conn
->uri
);
310 done_string(&prefix
);
312 const int errno_from_opendir
= errno
;
316 int file
= smbc_open(url
, O_RDONLY
, 0);
319 /* If we're opening the list of shares without
320 * proper authentication, then smbc_opendir
321 * fails with EACCES and smbc_open fails with
322 * ENOENT. In this case, return the EACCES so
323 * that the parent ELinks process will prompt
324 * for credentials. */
325 if (errno
== ENOENT
&& errno_from_opendir
== EACCES
)
326 errno
= errno_from_opendir
;
327 smb_error(connection_state_for_errno(errno
));
330 res
= smbc_fstat(file
, &sb
);
332 smb_error(connection_state_for_errno(res
));
335 fprintf(header_out
, "%" OFF_PRINT_FORMAT
,
336 (off_print_T
) sb
.st_size
);
339 fdout
= fileno(data_out
);
340 while ((r
= smbc_read(file
, buf
, READ_SIZE
)) > 0) {
341 if (safe_write(fdout
, buf
, r
) <= 0)
351 /* Kill the current connection and ask for a username/password for the next
354 prompt_username_pw(struct connection
*conn
)
356 add_auth_entry(conn
->uri
, "Samba", NULL
, NULL
, 0);
357 abort_connection(conn
, connection_state(S_OK
));
361 smb_got_error(struct socket
*socket
, struct read_buffer
*rb
)
363 int len
= rb
->length
;
364 struct connection
*conn
= socket
->conn
;
365 struct connection_state error
;
368 abort_connection(conn
, connection_state_for_errno(errno
));
372 /* There should be free space in the buffer, because
373 * @alloc_read_buffer allocated several kibibytes, and the
374 * child process wrote only an integer and a newline to the
376 assert(rb
->freespace
>= 1);
378 abort_connection(conn
, connection_state(S_INTERNAL
));
381 rb
->data
[len
] = '\0';
382 switch (rb
->data
[0]) {
384 error
= connection_state_for_errno(atoi(rb
->data
+ 1));
387 error
= connection_state(atoi(rb
->data
+ 1));
390 ERROR("malformed error code: %s", rb
->data
);
391 error
= connection_state(S_INTERNAL
);
394 kill_buffer_data(rb
, len
);
396 if (is_system_error(error
) && error
.syserr
== EACCES
)
397 prompt_username_pw(conn
);
399 abort_connection(conn
, error
);
403 smb_got_data(struct socket
*socket
, struct read_buffer
*rb
)
405 int len
= rb
->length
;
406 struct connection
*conn
= socket
->conn
;
409 abort_connection(conn
, connection_state_for_errno(errno
));
414 abort_connection(conn
, connection_state(S_OK
));
418 socket
->state
= SOCKET_END_ONCLOSE
;
419 conn
->received
+= len
;
420 if (add_fragment(conn
->cached
, conn
->from
, rb
->data
, len
) == 1)
423 kill_buffer_data(rb
, len
);
425 read_from_socket(socket
, rb
, connection_state(S_TRANS
), smb_got_data
);
429 smb_got_header(struct socket
*socket
, struct read_buffer
*rb
)
431 struct connection
*conn
= socket
->conn
;
432 struct read_buffer
*buf
;
435 conn
->cached
= get_cache_entry(conn
->uri
);
437 /* Even though these are pipes rather than real
438 * sockets, call close_socket instead of close, to
439 * ensure that abort_connection won't try to close the
440 * file descriptors again. (Could we skip the calls
441 * and assume abort_connection will do them?) */
442 close_socket(socket
);
443 close_socket(conn
->data_socket
);
444 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
447 socket
->state
= SOCKET_END_ONCLOSE
;
449 if (rb
->length
> 0) {
450 unsigned char *ctype
= memacpy(rb
->data
, rb
->length
);
452 if (ctype
&& *ctype
) {
453 if (!strcmp(ctype
, "text/x-error")) {
457 if (ctype
[0] >= '0' && ctype
[0] <= '9') {
459 conn
->est_length
= (off_t
)atoll(ctype
);
461 conn
->est_length
= (off_t
)atol(ctype
);
466 if (!conn
->est_length
) {
467 abort_connection(conn
, connection_state(S_OK
));
471 else mem_free_set(&conn
->cached
->content_type
, ctype
);
478 buf
= alloc_read_buffer(conn
->data_socket
);
480 close_socket(socket
);
481 close_socket(conn
->data_socket
);
482 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
486 mem_free_set(&conn
->cached
->content_type
, stracpy("text/html"));
487 read_from_socket(conn
->data_socket
, buf
,
488 connection_state(S_CONN
), smb_got_error
);
490 read_from_socket(conn
->data_socket
, buf
,
491 connection_state(S_CONN
), smb_got_data
);
496 close_all_fds_but_two(int header
, int data
)
503 if (!getrlimit(RLIMIT_NOFILE
, &lim
))
506 for (n
= 3; n
< max
; n
++) {
507 if (n
!= header
&& n
!= data
) close(n
);
514 smb_protocol_handler(struct connection
*conn
)
516 int smb_pipe
[2] = { -1, -1 };
517 int header_pipe
[2] = { -1, -1 };
520 if (c_pipe(smb_pipe
) || c_pipe(header_pipe
)) {
523 if (smb_pipe
[0] >= 0) close(smb_pipe
[0]);
524 if (smb_pipe
[1] >= 0) close(smb_pipe
[1]);
525 if (header_pipe
[0] >= 0) close(header_pipe
[0]);
526 if (header_pipe
[1] >= 0) close(header_pipe
[1]);
527 abort_connection(conn
, connection_state_for_errno(s_errno
));
531 conn
->unrestartable
= 1;
532 find_auth(conn
->uri
); /* remember username and password */
540 close(header_pipe
[0]);
541 close(header_pipe
[1]);
542 retry_connection(conn
, connection_state_for_errno(s_errno
));
547 dup2(open("/dev/null", O_RDONLY
), 0);
550 data_out
= fdopen(smb_pipe
[1], "w");
551 header_out
= fdopen(header_pipe
[1], "w");
553 if (!data_out
|| !header_out
) exit(1);
556 close(header_pipe
[0]);
558 /* There may be outgoing data in stdio buffers
559 * inherited from the parent process. The parent
560 * process is going to write this data, so the child
561 * process must not do that. Closing the file
562 * descriptors ensures this.
564 * FIXME: If something opens more files and gets the
565 * same file descriptors and does not close them
566 * before exit(), then stdio may attempt to write the
567 * buffers to the wrong files. This might happen for
568 * example if libsmbclient calls syslog(). */
570 close_all_fds_but_two(smb_pipe
[1], header_pipe
[1]);
574 struct read_buffer
*buf2
;
576 conn
->data_socket
->fd
= smb_pipe
[0];
577 conn
->socket
->fd
= header_pipe
[0];
578 set_nonblocking_fd(conn
->data_socket
->fd
);
579 set_nonblocking_fd(conn
->socket
->fd
);
581 close(header_pipe
[1]);
582 buf2
= alloc_read_buffer(conn
->socket
);
584 close_socket(conn
->data_socket
);
585 close_socket(conn
->socket
);
586 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
589 read_from_socket(conn
->socket
, buf2
,
590 connection_state(S_CONN
), smb_got_header
);