Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117747019
sync_server.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
28 KB
Referenced Files
None
Subscribers
None
sync_server.c
View Options
/* sync_server.c -- Cyrus synchronization server
*
* Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Original version written by David Carter <dpc22@cam.ac.uk>
* Rewritten and integrated into Cyrus by Ken Murchison <ken@oceana.com>
*/
#include
<config.h>
#ifdef HAVE_STDINT_H
#include
<stdint.h>
#endif
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<stdio.h>
#include
<string.h>
#include
<fcntl.h>
#include
<signal.h>
#include
<sys/types.h>
#include
<sys/wait.h>
#include
<sys/param.h>
#include
<sysexits.h>
#include
<syslog.h>
#include
<netdb.h>
#include
<sys/socket.h>
#include
<sys/un.h>
#include
<netinet/in.h>
#include
<netinet/tcp.h>
#include
<arpa/inet.h>
#include
<ctype.h>
#include
<sasl/sasl.h>
#include
<sasl/saslutil.h>
#include
"assert.h"
#include
"annotate.h"
#include
"append.h"
#include
"auth.h"
#ifdef WITH_DAV
#include
"dav_db.h"
#endif
/* WITH_DAV */
#include
"dlist.h"
#include
"global.h"
#include
"hash.h"
#include
"imparse.h"
#include
"mailbox.h"
#include
"map.h"
#include
"mboxlist.h"
#include
"partlist.h"
#include
"proc.h"
#include
"prot.h"
#include
"quota.h"
#include
"seen.h"
#include
"sync_log.h"
#include
"telemetry.h"
#include
"tls.h"
#include
"user.h"
#include
"util.h"
#include
"version.h"
#include
"xmalloc.h"
#include
"xstrlcat.h"
/* generated headers are not necessarily in current directory */
#include
"imap/imap_err.h"
#include
"message_guid.h"
#include
"sync_support.h"
/*#include "cdb.h"*/
extern
int
optind
;
extern
char
*
optarg
;
extern
int
opterr
;
/* for config.c */
const
int
config_need_data
=
CONFIG_NEED_PARTITION_DATA
;
static
sasl_ssf_t
extprops_ssf
=
0
;
#ifdef HAVE_SSL
static
SSL
*
tls_conn
;
#endif
/* HAVE_SSL */
static
sasl_conn_t
*
sync_saslconn
=
NULL
;
/* the sasl connection context */
static
char
*
sync_userid
=
0
;
static
struct
namespace
sync_namespace
;
static
struct
namespace
*
sync_namespacep
=
&
sync_namespace
;
static
struct
auth_state
*
sync_authstate
=
0
;
static
int
sync_userisadmin
=
0
;
static
const
char
*
sync_clienthost
=
"[local]"
;
static
struct
protstream
*
sync_out
=
NULL
;
static
struct
protstream
*
sync_in
=
NULL
;
static
int
sync_logfd
=
-1
;
static
int
sync_starttls_done
=
0
;
static
int
sync_compress_done
=
0
;
static
int
opt_force
=
0
;
/* commands that have specific names */
static
void
cmdloop
(
void
);
static
void
cmd_authenticate
(
char
*
mech
,
char
*
resp
);
static
void
cmd_starttls
(
void
);
static
void
cmd_restart
(
struct
sync_reserve_list
**
reserve_listp
,
int
realloc
);
static
void
cmd_compress
(
char
*
alg
);
/* generic commands - in dlist format */
static
void
cmd_get
(
struct
dlist
*
kl
);
static
void
cmd_apply
(
struct
dlist
*
kl
,
struct
sync_reserve_list
*
reserve_list
);
static
void
cmd_restore
(
struct
dlist
*
kin
,
struct
sync_reserve_list
*
reserve_list
);
static
void
usage
(
void
);
void
shut_down
(
int
code
)
__attribute__
((
noreturn
));
void
shut_down_via_signal
(
int
code
)
__attribute__
((
noreturn
));
extern
int
saslserver
(
sasl_conn_t
*
conn
,
const
char
*
mech
,
const
char
*
init_resp
,
const
char
*
resp_prefix
,
const
char
*
continuation
,
const
char
*
empty_resp
,
struct
protstream
*
pin
,
struct
protstream
*
pout
,
int
*
sasl_result
,
char
**
success_data
);
static
struct
saslprops_t
saslprops
=
SASLPROPS_INITIALIZER
;
/* the sasl proxy policy context */
static
struct
proxy_context
sync_proxyctx
=
{
0
,
1
,
&
sync_authstate
,
&
sync_userisadmin
,
NULL
};
static
struct
sasl_callback
mysasl_cb
[]
=
{
{
SASL_CB_GETOPT
,
(
mysasl_cb_ft
*
)
&
mysasl_config
,
NULL
},
{
SASL_CB_PROXY_POLICY
,
(
mysasl_cb_ft
*
)
&
mysasl_proxy_policy
,
(
void
*
)
&
sync_proxyctx
},
{
SASL_CB_CANON_USER
,
(
mysasl_cb_ft
*
)
&
mysasl_canon_user
,
NULL
},
{
SASL_CB_LIST_END
,
NULL
,
NULL
}
};
static
void
sync_reset
(
void
)
{
proc_cleanup
();
if
(
sync_in
)
{
prot_NONBLOCK
(
sync_in
);
prot_fill
(
sync_in
);
prot_free
(
sync_in
);
}
if
(
sync_out
)
{
prot_flush
(
sync_out
);
prot_free
(
sync_out
);
}
sync_in
=
sync_out
=
NULL
;
#ifdef HAVE_SSL
if
(
tls_conn
)
{
tls_reset_servertls
(
&
tls_conn
);
tls_conn
=
NULL
;
}
#endif
cyrus_reset_stdio
();
sync_clienthost
=
"[local]"
;
if
(
sync_logfd
!=
-1
)
{
close
(
sync_logfd
);
sync_logfd
=
-1
;
}
if
(
sync_userid
!=
NULL
)
{
free
(
sync_userid
);
sync_userid
=
NULL
;
}
if
(
sync_authstate
)
{
auth_freestate
(
sync_authstate
);
sync_authstate
=
NULL
;
}
if
(
sync_saslconn
)
{
sasl_dispose
(
&
sync_saslconn
);
sync_saslconn
=
NULL
;
}
sync_starttls_done
=
0
;
sync_compress_done
=
0
;
saslprops_reset
(
&
saslprops
);
}
/*
* run once when process is forked;
* MUST NOT exit directly; must return with non-zero error code
*/
int
service_init
(
int
argc
__attribute__
((
unused
)),
char
**
argv
__attribute__
((
unused
)),
char
**
envp
__attribute__
((
unused
)))
{
int
opt
,
r
;
if
(
geteuid
()
==
0
)
fatal
(
"must run as the Cyrus user"
,
EX_USAGE
);
setproctitle_init
(
argc
,
argv
,
envp
);
/* set signal handlers */
signals_set_shutdown
(
&
shut_down_via_signal
);
signal
(
SIGPIPE
,
SIG_IGN
);
/* load the SASL plugins */
global_sasl_init
(
1
,
1
,
mysasl_cb
);
while
((
opt
=
getopt
(
argc
,
argv
,
"p:f"
))
!=
EOF
)
{
switch
(
opt
)
{
case
'p'
:
/* external protection */
extprops_ssf
=
atoi
(
optarg
);
break
;
case
'f'
:
opt_force
=
1
;
break
;
default
:
usage
();
}
}
/* Set namespace -- force standard (internal) */
if
((
r
=
mboxname_init_namespace
(
sync_namespacep
,
1
))
!=
0
)
{
fatal
(
error_message
(
r
),
EX_CONFIG
);
}
mboxevent_setnamespace
(
sync_namespacep
);
return
0
;
}
/*
* Issue the capability banner
*/
static
void
dobanner
(
void
)
{
const
char
*
mechlist
;
int
mechcount
;
if
(
!
sync_userid
)
{
if
(
sasl_listmech
(
sync_saslconn
,
NULL
,
"* SASL "
,
" "
,
"
\r\n
"
,
&
mechlist
,
NULL
,
&
mechcount
)
==
SASL_OK
&&
mechcount
>
0
)
{
prot_printf
(
sync_out
,
"%s"
,
mechlist
);
}
if
(
tls_enabled
()
&&
!
sync_starttls_done
)
{
prot_printf
(
sync_out
,
"* STARTTLS
\r\n
"
);
}
#ifdef HAVE_ZLIB
if
(
!
sync_compress_done
&&
!
sync_starttls_done
)
{
prot_printf
(
sync_out
,
"* COMPRESS DEFLATE
\r\n
"
);
}
#endif
}
prot_printf
(
sync_out
,
"* OK %s Cyrus sync server %s
\r\n
"
,
config_servername
,
CYRUS_VERSION
);
prot_flush
(
sync_out
);
}
/*
* run for each accepted connection
*/
int
service_main
(
int
argc
__attribute__
((
unused
)),
char
**
argv
__attribute__
((
unused
)),
char
**
envp
__attribute__
((
unused
)))
{
const
char
*
localip
,
*
remoteip
;
sasl_security_properties_t
*
secprops
=
NULL
;
int
timeout
;
signals_poll
();
sync_in
=
prot_new
(
0
,
0
);
sync_out
=
prot_new
(
1
,
1
);
/* Force use of LITERAL+ so we don't need two way communications */
prot_setisclient
(
sync_in
,
1
);
prot_setisclient
(
sync_out
,
1
);
/* Find out name of client host */
sync_clienthost
=
get_clienthost
(
0
,
&
localip
,
&
remoteip
);
if
(
!
strcmp
(
sync_clienthost
,
UNIX_SOCKET
))
{
/* we're not connected to an internet socket! */
sync_userid
=
xstrdup
(
cyrus_user
());
sync_userisadmin
=
1
;
}
else
{
if
(
localip
&&
remoteip
)
{
buf_setcstr
(
&
saslprops
.
ipremoteport
,
remoteip
);
buf_setcstr
(
&
saslprops
.
iplocalport
,
localip
);
}
/* other params should be filled in */
if
(
sasl_server_new
(
"csync"
,
config_servername
,
NULL
,
buf_cstringnull_ifempty
(
&
saslprops
.
iplocalport
),
buf_cstringnull_ifempty
(
&
saslprops
.
ipremoteport
),
NULL
,
0
,
&
sync_saslconn
)
!=
SASL_OK
)
fatal
(
"SASL failed initializing: sasl_server_new()"
,
EX_TEMPFAIL
);
/* will always return something valid */
secprops
=
mysasl_secprops
(
SASL_SEC_NOANONYMOUS
);
if
(
sasl_setprop
(
sync_saslconn
,
SASL_SEC_PROPS
,
secprops
)
!=
SASL_OK
)
fatal
(
"Failed to set SASL property"
,
EX_TEMPFAIL
);
if
(
sasl_setprop
(
sync_saslconn
,
SASL_SSF_EXTERNAL
,
&
extprops_ssf
)
!=
SASL_OK
)
fatal
(
"Failed to set SASL property"
,
EX_TEMPFAIL
);
tcp_disable_nagle
(
1
);
/* XXX magic fd */
}
proc_register
(
config_ident
,
sync_clienthost
,
NULL
,
NULL
,
NULL
);
/* Set inactivity timer */
timeout
=
config_getduration
(
IMAPOPT_SYNC_TIMEOUT
,
's'
);
if
(
timeout
<
3
)
timeout
=
3
;
prot_settimeout
(
sync_in
,
timeout
);
prot_setflushonread
(
sync_in
,
sync_out
);
sync_log_init
();
if
(
!
config_getswitch
(
IMAPOPT_SYNC_LOG_CHAIN
))
sync_log_suppress
();
dobanner
();
cmdloop
();
/* EXIT executed */
/* cleanup */
sync_reset
();
return
0
;
}
/* Called by service API to shut down the service */
void
service_abort
(
int
error
)
{
shut_down
(
error
);
}
static
void
usage
(
void
)
{
prot_printf
(
sync_out
,
"* usage: sync_server [-C <alt_config>]
\r\n
"
);
prot_flush
(
sync_out
);
exit
(
EX_USAGE
);
}
/*
* Cleanly shut down and exit
*/
void
shut_down
(
int
code
)
{
in_shutdown
=
1
;
libcyrus_run_delayed
();
proc_cleanup
();
seen_done
();
partlist_local_done
();
if
(
sync_in
)
{
prot_NONBLOCK
(
sync_in
);
prot_fill
(
sync_in
);
prot_free
(
sync_in
);
}
if
(
sync_out
)
{
prot_flush
(
sync_out
);
prot_free
(
sync_out
);
}
#ifdef HAVE_SSL
tls_shutdown_serverengine
();
#endif
saslprops_free
(
&
saslprops
);
cyrus_done
();
exit
(
code
);
}
void
shut_down_via_signal
(
int
code
__attribute__
((
unused
)))
{
if
(
sync_out
)
{
prot_puts
(
sync_out
,
"BYE shutting down
\r\n
"
);
}
shut_down
(
0
);
}
EXPORTED
void
fatal
(
const
char
*
s
,
int
code
)
{
static
int
recurse_code
=
0
;
if
(
recurse_code
)
{
/* We were called recursively. Just give up */
proc_cleanup
();
exit
(
recurse_code
);
}
recurse_code
=
code
;
if
(
sync_out
)
{
prot_printf
(
sync_out
,
"* Fatal error: %s
\r\n
"
,
s
);
prot_flush
(
sync_out
);
}
syslog
(
LOG_ERR
,
"Fatal error: %s"
,
s
);
shut_down
(
code
);
}
/* Reset the given sasl_conn_t to a sane state */
static
int
reset_saslconn
(
sasl_conn_t
**
conn
)
{
int
ret
;
sasl_security_properties_t
*
secprops
=
NULL
;
sasl_dispose
(
conn
);
/* do initialization typical of service_main */
ret
=
sasl_server_new
(
"csync"
,
config_servername
,
NULL
,
buf_cstringnull_ifempty
(
&
saslprops
.
iplocalport
),
buf_cstringnull_ifempty
(
&
saslprops
.
ipremoteport
),
NULL
,
0
,
conn
);
if
(
ret
!=
SASL_OK
)
return
ret
;
secprops
=
mysasl_secprops
(
SASL_SEC_NOANONYMOUS
);
ret
=
sasl_setprop
(
*
conn
,
SASL_SEC_PROPS
,
secprops
);
if
(
ret
!=
SASL_OK
)
return
ret
;
/* end of service_main initialization excepting SSF */
/* If we have TLS/SSL info, set it */
if
(
saslprops
.
ssf
)
{
ret
=
saslprops_set_tls
(
&
saslprops
,
*
conn
);
}
else
{
ret
=
sasl_setprop
(
*
conn
,
SASL_SSF_EXTERNAL
,
&
extprops_ssf
);
}
if
(
ret
!=
SASL_OK
)
return
ret
;
/* End TLS/SSL Info */
return
SASL_OK
;
}
static
void
cmdloop
(
void
)
{
struct
sync_reserve_list
*
reserve_list
;
static
struct
buf
cmd
;
static
struct
buf
arg1
,
arg2
;
int
c
;
char
*
p
;
struct
dlist
*
kl
;
syslog
(
LOG_DEBUG
,
"cmdloop(): startup"
);
reserve_list
=
sync_reserve_list_create
(
SYNC_MESSAGE_LIST_HASH_SIZE
);
for
(;;)
{
prot_flush
(
sync_out
);
libcyrus_run_delayed
();
/* Parse command name */
if
((
c
=
getword
(
sync_in
,
&
cmd
))
==
EOF
)
break
;
if
(
!
cmd
.
s
[
0
])
{
prot_printf
(
sync_out
,
"BAD Null command
\r\n
"
);
eatline
(
sync_in
,
c
);
continue
;
}
if
(
Uislower
(
cmd
.
s
[
0
]))
cmd
.
s
[
0
]
=
toupper
((
unsigned
char
)
cmd
.
s
[
0
]);
for
(
p
=
&
cmd
.
s
[
1
];
*
p
;
p
++
)
{
if
(
Uisupper
(
*
p
))
*
p
=
tolower
((
unsigned
char
)
*
p
);
}
/* Must be an admin */
if
(
sync_userid
&&
!
sync_userisadmin
)
goto
noperm
;
switch
(
cmd
.
s
[
0
])
{
case
'A'
:
if
(
!
strcmp
(
cmd
.
s
,
"Authenticate"
))
{
int
haveinitresp
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
sync_in
,
&
arg1
);
if
(
!
imparse_isatom
(
arg1
.
s
))
{
prot_printf
(
sync_out
,
"BAD Invalid mechanism
\r\n
"
);
eatline
(
sync_in
,
c
);
continue
;
}
if
(
c
==
' '
)
{
haveinitresp
=
1
;
c
=
getword
(
sync_in
,
&
arg2
);
if
(
c
==
EOF
)
goto
missingargs
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
sync_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
if
(
sync_userid
)
{
prot_printf
(
sync_out
,
"BAD Already authenticated
\r\n
"
);
continue
;
}
cmd_authenticate
(
arg1
.
s
,
haveinitresp
?
arg2
.
s
:
NULL
);
continue
;
}
if
(
!
sync_userid
)
goto
nologin
;
if
(
!
strcmp
(
cmd
.
s
,
"Apply"
))
{
kl
=
sync_parseline
(
sync_in
);
if
(
kl
)
{
cmd_apply
(
kl
,
reserve_list
);
dlist_free
(
&
kl
);
}
else
{
xsyslog
(
LOG_ERR
,
"IOERROR: received bad command"
,
"command=<%s>"
,
cmd
.
s
);
prot_printf
(
sync_out
,
"BAD IMAP_PROTOCOL_ERROR Failed to parse APPLY line
\r\n
"
);
}
continue
;
}
break
;
case
'C'
:
if
(
!
strcmp
(
cmd
.
s
,
"Compress"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
sync_in
,
&
arg1
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
sync_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_compress
(
arg1
.
s
);
continue
;
}
break
;
case
'G'
:
if
(
!
sync_userid
)
goto
nologin
;
if
(
!
strcmp
(
cmd
.
s
,
"Get"
))
{
kl
=
sync_parseline
(
sync_in
);
if
(
kl
)
{
cmd_get
(
kl
);
dlist_free
(
&
kl
);
}
else
{
xsyslog
(
LOG_ERR
,
"IOERROR: received bad command"
,
"command=<%s>"
,
cmd
.
s
);
prot_printf
(
sync_out
,
"BAD IMAP_PROTOCOL_ERROR Failed to parse GET line
\r\n
"
);
}
continue
;
}
break
;
case
'E'
:
if
(
!
strcmp
(
cmd
.
s
,
"Exit"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
sync_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
prot_printf
(
sync_out
,
"OK Finished
\r\n
"
);
prot_flush
(
sync_out
);
goto
exit
;
}
break
;
case
'N'
:
if
(
!
strcmp
(
cmd
.
s
,
"Noop"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
sync_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
prot_printf
(
sync_out
,
"OK Noop completed
\r\n
"
);
continue
;
}
break
;
case
'R'
:
if
(
!
strcmp
(
cmd
.
s
,
"Restart"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
sync_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
/* just clear the GUID cache */
cmd_restart
(
&
reserve_list
,
1
);
prot_printf
(
sync_out
,
"OK Restarting
\r\n
"
);
continue
;
}
if
(
!
sync_userid
)
goto
nologin
;
if
(
!
strcmp
(
cmd
.
s
,
"Restore"
))
{
kl
=
sync_parseline
(
sync_in
);
if
(
kl
)
{
cmd_restore
(
kl
,
reserve_list
);
dlist_free
(
&
kl
);
}
else
{
xsyslog
(
LOG_ERR
,
"IOERROR: received bad command"
,
"command=<%s>"
,
cmd
.
s
);
prot_printf
(
sync_out
,
"BAD IMAP_PROTOCOL_ERROR Failed to parse RESTORE line
\r\n
"
);
}
continue
;
}
break
;
case
'S'
:
if
(
!
strcmp
(
cmd
.
s
,
"Starttls"
)
&&
tls_enabled
())
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
sync_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
/* XXX discard any input pipelined after STARTTLS */
prot_flush
(
sync_in
);
/* if we've already done SASL fail */
if
(
sync_userid
!=
NULL
)
{
prot_printf
(
sync_out
,
"BAD Can't Starttls after authentication
\r\n
"
);
continue
;
}
/* check if already did a successful tls */
if
(
sync_starttls_done
==
1
)
{
prot_printf
(
sync_out
,
"BAD Already did a successful Starttls
\r\n
"
);
continue
;
}
cmd_starttls
();
continue
;
}
break
;
}
xsyslog
(
LOG_ERR
,
"IOERROR: received bad command"
,
"command=<%s>"
,
cmd
.
s
);
prot_printf
(
sync_out
,
"BAD IMAP_PROTOCOL_ERROR Unrecognized command
\r\n
"
);
eatline
(
sync_in
,
c
);
continue
;
nologin
:
prot_printf
(
sync_out
,
"NO Please authenticate first
\r\n
"
);
eatline
(
sync_in
,
c
);
continue
;
noperm
:
prot_printf
(
sync_out
,
"NO %s
\r\n
"
,
error_message
(
IMAP_PERMISSION_DENIED
));
eatline
(
sync_in
,
c
);
continue
;
missingargs
:
prot_printf
(
sync_out
,
"BAD Missing required argument to %s
\r\n
"
,
cmd
.
s
);
eatline
(
sync_in
,
c
);
continue
;
extraargs
:
prot_printf
(
sync_out
,
"BAD Unexpected extra arguments to %s
\r\n
"
,
cmd
.
s
);
eatline
(
sync_in
,
c
);
continue
;
}
exit
:
cmd_restart
(
&
reserve_list
,
0
);
}
static
void
cmd_authenticate
(
char
*
mech
,
char
*
resp
)
{
int
r
,
sasl_result
;
sasl_ssf_t
ssf
;
const
char
*
ssfmsg
=
NULL
;
const
void
*
val
;
int
failedloginpause
;
if
(
sync_userid
)
{
prot_printf
(
sync_out
,
"BAD Already authenticated
\r\n
"
);
return
;
}
r
=
saslserver
(
sync_saslconn
,
mech
,
resp
,
""
,
"+ "
,
""
,
sync_in
,
sync_out
,
&
sasl_result
,
NULL
);
if
(
r
)
{
const
char
*
errorstring
=
NULL
;
const
char
*
userid
=
"-notset-"
;
switch
(
r
)
{
case
IMAP_SASL_CANCEL
:
prot_printf
(
sync_out
,
"BAD Client canceled authentication
\r\n
"
);
break
;
case
IMAP_SASL_PROTERR
:
errorstring
=
prot_error
(
sync_in
);
prot_printf
(
sync_out
,
"NO Error reading client response: %s
\r\n
"
,
errorstring
?
errorstring
:
""
);
break
;
default
:
/* failed authentication */
errorstring
=
sasl_errstring
(
sasl_result
,
NULL
,
NULL
);
if
(
r
!=
SASL_NOUSER
)
sasl_getprop
(
sync_saslconn
,
SASL_USERNAME
,
(
const
void
**
)
&
userid
);
syslog
(
LOG_NOTICE
,
"badlogin: %s %s (%s) [%s]"
,
sync_clienthost
,
mech
,
userid
,
sasl_errdetail
(
sync_saslconn
));
failedloginpause
=
config_getduration
(
IMAPOPT_FAILEDLOGINPAUSE
,
's'
);
if
(
failedloginpause
!=
0
)
{
sleep
(
failedloginpause
);
}
if
(
errorstring
)
{
prot_printf
(
sync_out
,
"NO %s
\r\n
"
,
errorstring
);
}
else
{
prot_printf
(
sync_out
,
"NO Error authenticating
\r\n
"
);
}
}
reset_saslconn
(
&
sync_saslconn
);
return
;
}
/* successful authentication */
/* get the userid from SASL --- already canonicalized from
* mysasl_proxy_policy()
*/
sasl_result
=
sasl_getprop
(
sync_saslconn
,
SASL_USERNAME
,
&
val
);
if
(
sasl_result
!=
SASL_OK
)
{
prot_printf
(
sync_out
,
"NO weird SASL error %d SASL_USERNAME
\r\n
"
,
sasl_result
);
syslog
(
LOG_ERR
,
"weird SASL error %d getting SASL_USERNAME"
,
sasl_result
);
reset_saslconn
(
&
sync_saslconn
);
return
;
}
sync_userid
=
xstrdup
((
const
char
*
)
val
);
proc_register
(
config_ident
,
sync_clienthost
,
sync_userid
,
NULL
,
NULL
);
syslog
(
LOG_NOTICE
,
"login: %s %s %s%s %s"
,
sync_clienthost
,
sync_userid
,
mech
,
sync_starttls_done
?
"+TLS"
:
""
,
"User logged in"
);
sasl_getprop
(
sync_saslconn
,
SASL_SSF
,
&
val
);
ssf
=
*
((
sasl_ssf_t
*
)
val
);
/* really, we should be doing a sasl_getprop on SASL_SSF_EXTERNAL,
but the current libsasl doesn't allow that. */
if
(
sync_starttls_done
)
{
switch
(
ssf
)
{
case
0
:
ssfmsg
=
"tls protection"
;
break
;
case
1
:
ssfmsg
=
"tls plus integrity protection"
;
break
;
default
:
ssfmsg
=
"tls plus privacy protection"
;
break
;
}
}
else
{
switch
(
ssf
)
{
case
0
:
ssfmsg
=
"no protection"
;
break
;
case
1
:
ssfmsg
=
"integrity protection"
;
break
;
default
:
ssfmsg
=
"privacy protection"
;
break
;
}
}
prot_printf
(
sync_out
,
"OK Success (%s)
\r\n
"
,
ssfmsg
);
prot_setsasl
(
sync_in
,
sync_saslconn
);
prot_setsasl
(
sync_out
,
sync_saslconn
);
/* Create telemetry log */
sync_logfd
=
telemetry_log
(
sync_userid
,
sync_in
,
sync_out
,
0
);
}
#ifdef HAVE_SSL
static
void
cmd_starttls
(
void
)
{
int
result
;
if
(
sync_starttls_done
==
1
)
{
prot_printf
(
sync_out
,
"NO %s
\r\n
"
,
"Already successfully executed STARTTLS"
);
return
;
}
result
=
tls_init_serverengine
(
"csync"
,
5
,
/* depth to verify */
1
,
/* can client auth? */
NULL
);
if
(
result
==
-1
)
{
syslog
(
LOG_ERR
,
"error initializing TLS"
);
prot_printf
(
sync_out
,
"NO %s
\r\n
"
,
"Error initializing TLS"
);
return
;
}
prot_printf
(
sync_out
,
"OK %s
\r\n
"
,
"Begin TLS negotiation now"
);
/* must flush our buffers before starting tls */
prot_flush
(
sync_out
);
result
=
tls_start_servertls
(
0
,
/* read */
1
,
/* write */
180
,
/* 3 minutes */
&
saslprops
,
&
tls_conn
);
/* if error */
if
(
result
==
-1
)
{
prot_printf
(
sync_out
,
"NO Starttls failed
\r\n
"
);
syslog
(
LOG_NOTICE
,
"STARTTLS failed: %s"
,
sync_clienthost
);
return
;
}
/* tell SASL about the negotiated layer */
result
=
saslprops_set_tls
(
&
saslprops
,
sync_saslconn
);
if
(
result
!=
SASL_OK
)
{
fatal
(
"saslprops_set_tls() failed: cmd_starttls()"
,
EX_TEMPFAIL
);
}
/* tell the prot layer about our new layers */
prot_settls
(
sync_in
,
tls_conn
);
prot_settls
(
sync_out
,
tls_conn
);
sync_starttls_done
=
1
;
dobanner
();
}
#else
static
void
cmd_starttls
(
void
)
{
fatal
(
"cmd_starttls() called, but no OpenSSL"
,
EX_SOFTWARE
);
}
#endif
/* HAVE_SSL */
#ifdef HAVE_ZLIB
static
void
cmd_compress
(
char
*
alg
)
{
if
(
sync_compress_done
)
{
prot_printf
(
sync_out
,
"NO Compression already active: %s
\r\n
"
,
alg
);
return
;
}
if
(
strcasecmp
(
alg
,
"DEFLATE"
))
{
prot_printf
(
sync_out
,
"NO Unknown compression algorithm: %s
\r\n
"
,
alg
);
return
;
}
if
(
ZLIB_VERSION
[
0
]
!=
zlibVersion
()[
0
])
{
prot_printf
(
sync_out
,
"NO Error initializing %s "
"(incompatible zlib version)
\r\n
"
,
alg
);
return
;
}
prot_printf
(
sync_out
,
"OK %s active
\r\n
"
,
alg
);
prot_flush
(
sync_out
);
prot_setcompress
(
sync_in
);
prot_setcompress
(
sync_out
);
sync_compress_done
=
1
;
}
#else
static
void
cmd_compress
(
char
*
alg
__attribute__
((
unused
)))
{
prot_printf
(
sync_out
,
"NO ZLIB not available
\r\n
"
);
}
#endif
/* ====================================================================== */
/* partition_list is simple linked list of names used by cmd_restart */
struct
partition_list
{
struct
partition_list
*
next
;
char
*
name
;
};
static
struct
partition_list
*
partition_list_add
(
char
*
name
,
struct
partition_list
*
pl
)
{
struct
partition_list
*
p
;
/* Is name already on list? */
for
(
p
=
pl
;
p
;
p
=
p
->
next
)
{
if
(
!
strcmp
(
p
->
name
,
name
))
return
(
pl
);
}
/* Add entry to start of list and return new list */
p
=
xzmalloc
(
sizeof
(
struct
partition_list
));
p
->
next
=
pl
;
p
->
name
=
xstrdup
(
name
);
return
(
p
);
}
static
void
partition_list_free
(
struct
partition_list
*
current
)
{
while
(
current
)
{
struct
partition_list
*
next
=
current
->
next
;
free
(
current
->
name
);
free
(
current
);
current
=
next
;
}
}
static
void
cmd_restart
(
struct
sync_reserve_list
**
reserve_listp
,
int
re_alloc
)
{
struct
sync_reserve
*
res
;
struct
sync_reserve_list
*
l
=
*
reserve_listp
;
struct
sync_msgid
*
msg
;
int
hash_size
=
l
->
hash_size
;
struct
partition_list
*
p
,
*
pl
=
NULL
;
for
(
res
=
l
->
head
;
res
;
res
=
res
->
next
)
{
for
(
msg
=
res
->
list
->
head
;
msg
;
msg
=
msg
->
next
)
{
if
(
!
msg
->
fname
)
continue
;
pl
=
partition_list_add
(
res
->
part
,
pl
);
unlink
(
msg
->
fname
);
}
}
sync_reserve_list_free
(
reserve_listp
);
/* Remove all <partition>/sync./<pid> directories referred to above */
for
(
p
=
pl
;
p
;
p
=
p
->
next
)
{
static
char
buf
[
MAX_MAILBOX_PATH
];
snprintf
(
buf
,
MAX_MAILBOX_PATH
,
"%s/sync./%lu"
,
config_partitiondir
(
p
->
name
),
(
unsigned
long
)
getpid
());
rmdir
(
buf
);
}
partition_list_free
(
pl
);
if
(
re_alloc
)
*
reserve_listp
=
sync_reserve_list_create
(
hash_size
);
else
*
reserve_listp
=
NULL
;
}
/******************************************************************************/
static
void
cmd_apply
(
struct
dlist
*
kin
,
struct
sync_reserve_list
*
reserve_list
)
{
struct
sync_state
sync_state
=
{
sync_userid
,
sync_userisadmin
,
sync_authstate
,
&
sync_namespace
,
sync_out
,
0
/* local_only */
};
const
char
*
resp
=
sync_apply
(
kin
,
reserve_list
,
&
sync_state
);
sync_checkpoint
(
sync_in
);
prot_printf
(
sync_out
,
"%s
\r\n
"
,
resp
);
}
static
void
cmd_get
(
struct
dlist
*
kin
)
{
struct
sync_state
sync_state
=
{
sync_userid
,
sync_userisadmin
,
sync_authstate
,
&
sync_namespace
,
sync_out
,
0
/* local_only */
};
const
char
*
resp
=
sync_get
(
kin
,
&
sync_state
);
prot_printf
(
sync_out
,
"%s
\r\n
"
,
resp
);
}
static
void
cmd_restore
(
struct
dlist
*
kin
,
struct
sync_reserve_list
*
reserve_list
)
{
struct
sync_state
sync_state
=
{
sync_userid
,
sync_userisadmin
,
sync_authstate
,
&
sync_namespace
,
sync_out
,
0
/* local_only */
};
const
char
*
resp
=
sync_restore
(
kin
,
reserve_list
,
&
sync_state
);
sync_checkpoint
(
sync_in
);
prot_printf
(
sync_out
,
"%s
\r\n
"
,
resp
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Apr 4 2026, 12:03 AM (4 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18821610
Default Alt Text
sync_server.c (28 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline