diff options
-rw-r--r-- | src/config.c | 54 | ||||
-rw-r--r-- | src/directive.c | 42 | ||||
-rw-r--r-- | src/diskio.c | 35 | ||||
-rw-r--r-- | src/exec.c | 2 | ||||
-rw-r--r-- | src/gpg.c | 80 | ||||
-rw-r--r-- | src/method.c | 2 | ||||
-rw-r--r-- | src/process.c | 2 | ||||
-rw-r--r-- | src/sql.c | 11 | ||||
-rw-r--r-- | src/sql.h | 2 | ||||
-rw-r--r-- | src/triplet.c | 86 | ||||
-rw-r--r-- | src/verify.c | 34 | ||||
-rw-r--r-- | src/wydawca.c | 2 | ||||
-rw-r--r-- | src/wydawca.h | 5 |
13 files changed, 262 insertions, 95 deletions
diff --git a/src/config.c b/src/config.c index ae7c974..8c4afd1 100644 --- a/src/config.c +++ b/src/config.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ static struct access_method default_gpg_key_method; #define skip_ws(s) while (*(s) && isspace (*(s))) (s)++; #define skip_word(s) while (*(s) && !isspace (*(s))) (s)++; +/* Return a pointer to the next word from *PSTR. Advanse *PSTR to the beginning + of next word, or to the terminating \0, whichever comes first. */ static char * get_word (char **pstr) { @@ -49,6 +51,8 @@ get_word (char **pstr) return word; } +/* Convert a boolean from STR into its C representation. If STR is a valid + boolean, store the result in *PVAL and return 0. Otherwise, return 1 */ static int get_bool (char *str, int *pval) { @@ -77,6 +81,7 @@ get_bool (char *str, int *pval) /home/user/../smith --> /home/smith /home/user/../.. --> / + ../file --> NULL */ char * safe_file_name (char *file_name) @@ -96,32 +101,49 @@ safe_file_name (char *file_name) /* delete trailing delimiter if any */ if (len && file_name[len-1] == '/') file_name[len-1] = 0; - - /* Eliminate any /../ */ + + /* Eliminate any ./ and /../ */ for (p = strchr (file_name, '.'); p; p = strchr (p, '.')) { - if (p > file_name && p[-1] == '/') + if (p[1] == '/' && (p == file_name || p[-1] == '/')) { - if (p[1] == '.' && (p[2] == 0 || p[2] == '/')) + char *q, *s; + + s = p + 2; + q = p; + while ((*q++ = *s++)) + ; + continue; + } + else if (p[1] == '.' && (p[2] == 0 || p[2] == '/')) + { + if (p == file_name) + return NULL; + if (p[-1] == '/') /* found */ { char *q, *s; + s = p + 2; + /* Find previous delimiter */ for (q = p-2; *q != '/' && q >= file_name; q--) ; if (q < file_name) - break; + { + q = file_name; + s++; + } + /* Copy stuff */ - s = p + 2; p = q; while ((*q++ = *s++)) ; continue; } } - + p++; } @@ -134,10 +156,15 @@ safe_file_name (char *file_name) return file_name; } +/* Same as safe_file_name, but returns an allocated copy. */ char * safe_file_name_alloc (const char *file_name) { - return safe_file_name (xstrdup (file_name)); + char *s = xstrdup (file_name); + char *ns = safe_file_name (s); + if (!ns) + free (s); + return ns; } @@ -329,6 +356,13 @@ cfg_archive (gsc_config_file_t *file, char *kw, char *val, void *data) return; } dp->archive.name = safe_file_name (xstrdup (word)); + if (!dp->archive.name) + { + file->error_msg (file->file_name, file->line, + "invalid archive name: %s", word); + file->error_count++; + return; + } word = get_word (&val); @@ -544,7 +578,7 @@ cfg_tar_program (gsc_config_file_t *file, char *kw, char *val, void *unused) { char *word = safe_file_name (get_word (&val)); - if (word[0] != '/') + if (!word || word[0] != '/') { file->error_msg (file->file_name, file->line, "must be an absolute file name"); diff --git a/src/directive.c b/src/directive.c index 0853552..6337a17 100644 --- a/src/directive.c +++ b/src/directive.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -22,7 +22,7 @@ int directive_parse (struct file_triplet *trp) { - size_t dcount, i; + size_t dcount, i, j; char *p; if (debug_level > 2) @@ -36,27 +36,27 @@ directive_parse (struct file_triplet *trp) trp->directive = xcalloc (dcount + 1, sizeof trp->directive[0]); p = trp->blurb; - for (i = 0; i < dcount; ) + for (i = j = 0; i < dcount; i++) { - trp->directive[i] = p; + trp->directive[j] = p; p = strchr (p, '\n'); if (p) *p++ = 0; - if (trim (trp->directive[i]) == 0) /* ignore empty lines */ + if (trim (trp->directive[j]) == 0) /* ignore empty lines */ continue; - if (strchr (trp->directive[i], ':') == NULL) + if (strchr (trp->directive[j], ':') == NULL) { logmsg (LOG_ERR, "%s: invalid line: %s", - trp->file[file_directive].name, trp->directive[i]); + trp->file[file_directive].name, trp->directive[j]); free (trp->directive); trp->directive = NULL; return 1; } - i++; + j++; if (!p) break; } - trp->directive[i] = NULL; + trp->directive[j] = NULL; return 0; } @@ -325,12 +325,26 @@ process_directives (struct file_triplet *trp, struct directory_pair *dpair) case directory_dir: relative_dir = safe_file_name_alloc (val); + if (!relative_dir || relative_dir[0] == '/') + { + logmsg (LOG_ERR, "%s: invalid directive", + trp->file[file_directive].name); + return 1; + } break; case filename_dir: - if (move_file (trp, dpair, file_dist, relative_dir) - || move_file (trp, dpair, file_signature, relative_dir)) - return 1; + if (verify_detached_signature (trp, dpair) == 0) + { + if (move_file (trp, dpair, file_dist, relative_dir) + || move_file (trp, dpair, file_signature, relative_dir)) + return 1; + } + else + { + logmsg (LOG_ERR, "invalid detached signature for %s", trp->name); + return 1; + } break; case version_dir: @@ -372,8 +386,8 @@ process_directives (struct file_triplet *trp, struct directory_pair *dpair) break; case rmsymlink_dir: - logmsg (LOG_CRIT, "%s: %s directive is not supported yet", - trp->file[file_directive].name, key); + if (rmsymlink_file (trp, dpair, relative_dir, val)) + return 1; } } diff --git a/src/diskio.c b/src/diskio.c index cb8df71..0c65792 100644 --- a/src/diskio.c +++ b/src/diskio.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -24,11 +24,16 @@ # endif #endif -/* Return true if ARG is a sub-directory of DIR */ +/* Return true if ARG is NULL or is a sub-directory of DIR */ int sub_dir_p (char *arg, char *dir) { - int dlen = strlen (dir); + int dlen; + + if (!arg) + return 0; + + dlen = strlen (dir); return strlen (arg) > dlen && memcmp (dir, arg, dlen) == 0 @@ -518,19 +523,19 @@ symlink_file (struct file_triplet *trp, struct directory_pair *dpair, } src = safe_file_name_alloc (wanted_src); - if (!sub_dir_p (src, dpair->dest_dir)) + if (!src || src[0] == '/') { logmsg (LOG_ERR, "symlink source `%s' does not lie under `%s'", - src, dpair->dest_dir); + wanted_src, dpair->dest_dir); free (src); return 1; } dst = safe_file_name_alloc (wanted_dst); - if (!sub_dir_p (dst, dpair->dest_dir)) + if (!dst || dst[0] == '/') { logmsg (LOG_ERR, "symlink destination `%s' does not lie under `%s'", - dst, dpair->dest_dir); + wanted_dst, dpair->dest_dir); free (src); free (dst); return 1; @@ -559,11 +564,17 @@ symlink_file (struct file_triplet *trp, struct directory_pair *dpair, if (rc == 0) { - rc = link (src, dst); - if (rc) - logmsg (LOG_ERR, - "symlinking %s to %s in directory %s failed: %s", - src, dst, dst_dir, strerror (errno)); + if (chdir (dst_dir)) + logmsg (LOG_ERR, "cannot change to %s: %s", + dst_dir, strerror (errno)); + else + { + rc = symlink (src, dst); + if (rc) + logmsg (LOG_ERR, + "symlinking %s to %s in directory %s failed: %s", + src, dst, dst_dir, strerror (errno)); + } } } @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -147,6 +147,57 @@ wydawca_gpg_homedir () return 0; } +static int +gpg_sig_ok_p (gpgme_ctx_t ctx, gpgme_signature_t sig) +{ + if (!sig) + return 0; + + for (; sig; sig = sig->next) + { + const char *uid; + gpgme_key_t key; + + if (gpgme_get_key (ctx, sig->fpr, &key, 0) == GPG_ERR_NO_ERROR) + uid = key->uids->uid; + else + uid = sig->fpr; + + switch (gpg_err_code (sig->status)) + { + case GPG_ERR_NO_ERROR: + if (debug_level) + logmsg (LOG_NOTICE, "Good signature from %s", uid); + break; + + case GPG_ERR_BAD_SIGNATURE: + logmsg (LOG_ERR, "BAD signature from %s", uid); + return 0; + + case GPG_ERR_NO_PUBKEY: + logmsg (LOG_ERR, "No public key"); + return 0; + + case GPG_ERR_NO_DATA: + logmsg (LOG_ERR, "No signature"); + return 0; + + case GPG_ERR_SIG_EXPIRED: + logmsg (LOG_ERR, "Expired signature from %s", uid); + return 0; + + case GPG_ERR_KEY_EXPIRED: + logmsg (LOG_ERR, "Key expired (%s)", uid); + return 0; + + default: + logmsg (LOG_ERR, "Unknown signature error"); + return 0; + } + } + return 1; +} + /* Verify the directive file from TRP using public key PUBKEY */ /* FIXME: dpair currently unused */ int @@ -171,14 +222,23 @@ verify_directive_signature (struct file_triplet *trp, ec = gpgme_op_verify (ctx, directive_data, NULL, plain); if (ec == GPG_ERR_NO_ERROR) { - size = gpgme_data_seek (plain, 0, SEEK_END); - gpgme_data_seek (plain, 0, SEEK_SET); - trp->blurb = xmalloc (size + 1); - gpgme_data_read (plain, trp->blurb, size); - trp->blurb[size] = 0; - gpgme_data_release (plain); - - rc = directive_parse (trp); + gpgme_verify_result_t result; + gpgme_signature_t sig; + + result = gpgme_op_verify_result (ctx); + if (gpg_sig_ok_p (ctx, result->signatures)) + { + size = gpgme_data_seek (plain, 0, SEEK_END); + gpgme_data_seek (plain, 0, SEEK_SET); + trp->blurb = xmalloc (size + 1); + gpgme_data_read (plain, trp->blurb, size); + trp->blurb[size] = 0; + gpgme_data_release (plain); + + rc = directive_parse (trp); + } + else + rc = 1; } else { @@ -230,7 +290,7 @@ verify_detached_signature (struct file_triplet *trp, return 0; case exec_fail: - logmsg (LOG_ERR, "bad detached signature for %s", trp->name); + logmsg (LOG_ERR, "BAD detached signature for %s", trp->name); break; case exec_error: diff --git a/src/method.c b/src/method.c index 433bf6a..45dea5a 100644 --- a/src/method.c +++ b/src/method.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it diff --git a/src/process.c b/src/process.c index 5452cd9..b7ead65 100644 --- a/src/process.c +++ b/src/process.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -17,6 +17,7 @@ #include "wydawca.h" #include "sql.h" +/* Singly-linked list of configured MySQL connections. */ struct sql_list { struct sql_list *next; @@ -25,6 +26,7 @@ struct sql_list static struct sql_list *sql_list; +/* Append CONN to the end of sql_list */ void sql_register_conn (struct sqlconn *conn) { @@ -34,6 +36,7 @@ sql_register_conn (struct sqlconn *conn) sql_list = ent; } +/* Find a configured connection that has the given IDENT */ struct sqlconn * sql_find_connection (const char *ident) { @@ -44,12 +47,14 @@ sql_find_connection (const char *ident) return NULL; } +/* Return true if there exists a connection with the given IDENT */ int sql_connection_exists_p (const char *ident) { return sql_find_connection (ident) != NULL; } +/* Initialize MySQL access method */ int sql_init_method (struct access_method *method) { @@ -74,7 +79,8 @@ sql_init_method (struct access_method *method) method->v.sqlconn = conn; return 0; } - + +/* Finish the initialized MySQL access method */ int sql_done_method (struct access_method *method) { @@ -82,6 +88,7 @@ sql_done_method (struct access_method *method) return 0; } +/* Execute QUERY using the given access METHOD. Return 0 on success. */ int sql_run_method (struct access_method *method, const char *query) { @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it diff --git a/src/triplet.c b/src/triplet.c index 3ecee29..b6b3ad2 100644 --- a/src/triplet.c +++ b/src/triplet.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -106,23 +106,46 @@ triplet_expired_p (struct file_triplet *trp, time_t ttl) return false; } -/* Return true if TRP is a complete and valid triplet */ -static bool -valid_triplet_p (struct file_triplet *trp) -{ - if (trp->file[file_dist].name - && trp->file[file_signature].name - && trp->file[file_directive].name) + +enum triplet_state + { + triplet_directive, /* Short triplet: only a directive is present, + but nothing more is required */ + triplet_complete, /* A complete triplet: all three files are present + and have the same owner */ + triplet_incomplete, /* Incomplete triplet: some files are missing */ + triplet_bad, /* Bad triplet. Should be removed immediately. */ + }; + + +static enum triplet_state +check_triplet_state (struct file_triplet *trp) +{ + if (trp->file[file_directive].name) { - if (trp->file[file_dist].uid == trp->file[file_signature].uid - && trp->file[file_dist].uid == trp->file[file_directive].uid) - return true; - else if (debug_level) - logmsg (LOG_DEBUG, "%s: invalid triplet: UIDs differ", trp->name); + if (trp->file[file_dist].name == 0 + && trp->file[file_signature].name == 0) + { + if (directive_get_value (trp, "filename", NULL)) + return triplet_directive ; + } + else if (trp->file[file_dist].name + && trp->file[file_signature].name) + { + if (trp->file[file_dist].uid == trp->file[file_signature].uid + && trp->file[file_dist].uid == trp->file[file_directive].uid) + return triplet_complete; + else + { + if (debug_level) + logmsg (LOG_DEBUG, "%s: invalid triplet: UIDs differ", + trp->name); + return triplet_bad; + } + } } - else if (debug_level) - logmsg (LOG_DEBUG, "%s: incomplete triplet", trp->name); - return false; + + return triplet_incomplete; } /* Unlink all parts of the triplet TRP */ @@ -157,17 +180,30 @@ triplet_processor (void *data, void *proc_data) SP (trp->file[file_signature].name), SP (trp->file[file_directive].name)); - if (valid_triplet_p (trp)) + if (verify_directive_file (trp, dpair) == 0) { - if (debug_level) - logmsg (LOG_DEBUG, "Processing triplet `%s'", trp->name); - if (verify_triplet (trp, dpair) == 0) - process_directives (trp, dpair); - /* FIXME: if only directive file exists? */ - else - remove_triplet (trp); + switch (check_triplet_state (trp)) + { + case triplet_directive: + case triplet_complete: + if (debug_level) + logmsg (LOG_DEBUG, "Processing triplet `%s'", trp->name); + process_directives (trp, dpair); + return true; + + case triplet_incomplete: + if (debug_level) + logmsg (LOG_DEBUG, "%s: incomplete triplet", trp->name); + /* ignore unless expired (see below); */ + break; + + case triplet_bad: + remove_triplet (trp); + return true; + } } - else if (triplet_expired_p (trp, dpair->file_sweep_time)) + + if (triplet_expired_p (trp, dpair->file_sweep_time)) remove_triplet (trp); return true; diff --git a/src/verify.c b/src/verify.c index f64183a..7869618 100644 --- a/src/verify.c +++ b/src/verify.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -38,6 +38,8 @@ trim (char *str) return len; } +/* Quote non-printable characters in INPUT. Point *OUTPUT to the malloc'ed + quoted string. Return its length. */ static size_t quote_string (struct access_method *method, const char *input, char **poutput) { @@ -73,6 +75,10 @@ quote_string (struct access_method *method, const char *input, char **poutput) return size; } +/* Expand PARAM by replacing %u with the quoted value of USER, %p with that + of PROJECT and %% with a single %. Return the malloc'ed result. + FIXME: Should we quote PARAM itself? +*/ char * expand_param (const char *param, const char *user, const char *project, struct access_method *method) @@ -157,7 +163,9 @@ expand_param (const char *param, const char *user, const char *project, } - + +/* Verify if USER has upload rights on the directory (project) requested + by TRP */ int check_access_rights (struct file_triplet *trp, struct directory_pair *dpair, const char *user) @@ -216,15 +224,19 @@ check_access_rights (struct file_triplet *trp, struct directory_pair *dpair, } int -verify_triplet (struct file_triplet *trp, struct directory_pair *dpair) +verify_directive_file (struct file_triplet *trp, struct directory_pair *dpair) { - struct passwd *pw = getpwuid (TRIPLET_UID (trp)); + struct passwd *pw; char *user_name; char *command; struct access_method *method = &dpair->gpg_key_method; const char *pubkey; int rc; + + if (!trp->file[file_directive].name) + return 1; + pw = getpwuid (TRIPLET_UID (trp)); if (!pw) { logmsg (LOG_ERR, "%s: getpwuid failed: %s", @@ -258,6 +270,8 @@ verify_triplet (struct file_triplet *trp, struct directory_pair *dpair) logmsg (LOG_ERR, "invalid signature for %s", trp->name); return 1; } + else if (debug_level) + logmsg (LOG_DEBUG, "%s: directive file signature OK", trp->name); if (debug_level > 1) { @@ -269,16 +283,6 @@ verify_triplet (struct file_triplet *trp, struct directory_pair *dpair) if (verify_directive_format (trp)) return 1; - if (check_access_rights (trp, dpair, user_name)) - return 1; - - if (verify_detached_signature (trp, dpair)) - { - logmsg (LOG_ERR, "invalid detached signature for %s", trp->name); - return 1; - } - - if (debug_level) - logmsg (LOG_DEBUG, "%s: triplet verified successfully", trp->name); return 0; } + diff --git a/src/wydawca.c b/src/wydawca.c index f514679..9918362 100644 --- a/src/wydawca.c +++ b/src/wydawca.c @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it diff --git a/src/wydawca.h b/src/wydawca.h index 918f6e0..cb42357 100644 --- a/src/wydawca.h +++ b/src/wydawca.h @@ -1,4 +1,4 @@ -/* wydawca - FTP release synchronisation daemon +/* wydawca - FTP release synchronization daemon Copyright (C) 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it @@ -199,7 +199,8 @@ void method_copy_result (struct access_method *method, const char *res, size_t size); /* Verification functions */ -int verify_triplet (struct file_triplet *trp, struct directory_pair *dpair); +int verify_directive_file (struct file_triplet *trp, + struct directory_pair *dpair); int verify_directive_signature (struct file_triplet *trp, struct directory_pair *dpair, const char *pubkey); |