From 3b9ea77879329e225f0f0b9653e44c48c14add77 Mon Sep 17 00:00:00 2001 From: Nikita Osokin Date: Tue, 14 Jan 2025 23:46:12 +0500 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=B4=D0=B2=D0=B5=20=D1=83=D1=8F=D0=B7?= =?UTF-8?q?=D0=B2=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8:=20=20*=20=D0=92?= =?UTF-8?q?=D0=B5=D0=B1-=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=82=D0=BA=D0=BB=D0=BE=D0=BD=D1=8F=D0=BB=20?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=BF=D0=B0=D1=80=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=20=D0=B4=D0=BB=D0=B8=D0=BD=D0=BD=D0=BE=D0=B9=200.=20chpa?= =?UTF-8?q?sswd.c=20=D0=BF=D1=80=D0=B5=D0=B4=D0=BF=D0=BE=D0=BB=D0=B0=D0=B3?= =?UTF-8?q?=D0=B0=D0=B5=D1=82,=20=D1=87=D1=82=D0=BE=20=D0=B4=D0=BB=D0=B8?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=D0=B0=20=D0=B3?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=B0=20=D1=85?= =?UTF-8?q?=D0=B5=D1=88=D0=B0=20=D0=BF=D0=B0=D1=80=D0=BE=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=85=D0=BE=D1=82=D1=8F=20=D0=B1=D1=8B=201,=20=20=20=20=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=BF=D1=80=D0=B8=20=D1=82=D0=B0=D0=BA=D0=BE=D0=BC=20?= =?UTF-8?q?=D0=BF=D0=B0=D1=80=D0=BE=D0=BB=D0=B5=20=D0=B5=D0=B3=D0=BE=20?= =?UTF-8?q?=D0=B4=D0=BB=D0=B8=D0=BD=D0=B0=20=D1=82=D0=BE=D0=B6=D0=B5=200.?= =?UTF-8?q?=20=D0=A2=D0=B0=D0=BA=D0=B8=D0=BC=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=BE=D0=BC=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=D1=81=D1=8F=20=D0=BD=D0=B5=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D0=B9=20=D1=84=D0=B0=D0=B9=D0=BB=20creds.=20=20*?= =?UTF-8?q?=20=D0=94=D0=B0=D0=B6=D0=B5=20=D0=BD=D0=B5=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D1=8B=20=D0=B7=D0=B0=D1=81=D1=82=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=BB=D0=B8=20chpasswd.c=20=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D1=80=D0=B5=D0=B7=D0=B5=D1=80=D0=B2=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BA=D0=BE=D0=BF=D0=B8=D0=B8=20=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D0=B0=20creds.=20=D0=9F=D0=BE=D1=8D=D1=82=D0=BE?= =?UTF-8?q?=D0=BC=D1=83,=20=D0=BB=D1=8E=D0=B1=D0=BE=D0=B9=20=D1=87=D0=B5?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2=D0=B5=D0=BA=20=D0=BC=D0=BE=D0=B3=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D1=80=D1=88=D0=B8=D1=82=D1=8C=20DoS=20=20=20?= =?UTF-8?q?=20=D0=B0=D1=82=D0=B0=D0=BA=D1=83,=20=D0=BE=D1=82=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=B2=20=D0=BE=D1=87=D0=B5=D0=BD=D1=8C=20?= =?UTF-8?q?=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D0=BE=D0=B2=20=D0=B8=20=D0=B7=D0=B0=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D0=B2=20chpasswd.c=20=D1=81=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=BE=D1=87=D0=B5=D0=BD=D1=8C=20=D0=BC?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=80=D0=B5=D0=B7=D0=B5=D1=80=D0=B2?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=BA=D0=BE=D0=BF=D0=B8=D0=B9.=20=D0=A2?= =?UTF-8?q?=D0=B5=D0=BF=D0=B5=D1=80=D1=8C,=20creds=20=D0=BA=D0=BE=D0=BF?= =?UTF-8?q?=D0=B8=D1=80=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D1=82=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=BA=D0=BE=20=20=20=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Немного отформатирован файл chpasswd.c, а файл webserver.go был перетабулирован. --- chpasswd.c | 70 ++++++++++++++-------- index.html | 2 +- webserver.go | 164 +++++++++++++++++++++++++-------------------------- 3 files changed, 130 insertions(+), 106 deletions(-) diff --git a/chpasswd.c b/chpasswd.c index c0208dd..70f2821 100644 --- a/chpasswd.c +++ b/chpasswd.c @@ -36,6 +36,7 @@ #define USER_NOT_FOUND -3 #define INCORRECT_OLD_PASSWORD -4 #define PASSWORD_MISMATCH -5 +#define INVALID_INPUT -6 #define OLD_PATH_BASE 20 #define BUF_LEN 64 * 1024 @@ -61,42 +62,41 @@ int open_creds_old(size_t i) { return -1; } -void copy_to(FILE * restrict src, FILE * restrict dest, const char * destPath) { +int copy_to(FILE * restrict src, FILE * restrict dest) { size_t readen /* неправильные глаголы... */, written; while(!feof(src)) { readen = fread(buf, 1, BUF_LEN, src); written = fwrite(buf, 1, readen, dest); if(written != readen) { - fclose(src); - fclose(dest); - remove(destPath); - - exit(INTERNAL_ERROR); + return 0; } } + + return 1; } -void backup(FILE * restrict creds) { +int backup(FILE * restrict creds) { int creds_old_fd = open_creds_old(OLD_PATH_BASE); if(creds_old_fd == -1) { - fclose(creds); - - exit(INTERNAL_ERROR); + return 0; } FILE * creds_old = fdopen(creds_old_fd, "w"); if(creds_old == NULL) { - fclose(creds); close(creds_old_fd); remove(creds_old_path); - exit(INTERNAL_ERROR); + return 0; } - copy_to(creds, creds_old, creds_old_path); + int ret = copy_to(creds, creds_old); fclose(creds_old); - rewind(creds); + if(!ret) { + remove(creds_old_path); + } + + return ret; } char * found_user_line( @@ -176,8 +176,6 @@ int main(int argc, char ** argv) { return INTERNAL_ERROR; } - backup(creds); - // Формирование нового файла creds FILE * creds_new = fopen("/etc/mail/creds.new", "w"); @@ -189,16 +187,18 @@ int main(int argc, char ** argv) { char * line = NULL; size_t n; - char * hash_begin, * hash_iter; + char * hash_begin, * hash_iter, * enc; + // Ищем и изменяем строку пользователя while((hash_begin = found_user_line(&line, &n, creds, username, creds_new)) == NULL); fputs(username, creds_new); fputc(':', creds_new); for(hash_iter = hash_begin; *hash_iter != ':'; hash_iter++); - *hash_iter = '\0'; // Теперь мы ограничили часть строки, которая содержит хеш старого пароля + *hash_iter = '\0'; // Теперь мы ограничили часть строки, которая содержит хеш старого пароля + // и можем сравнить его с хешем данного пароля - char * enc = crypt(old_password, hash_begin); + enc = crypt(old_password, hash_begin); if(strcmp(enc, hash_begin)) { fclose(creds); fclose(creds_new); @@ -208,6 +208,7 @@ int main(int argc, char ** argv) { } + // Получаем хеш нового пароля int inpipe[2], outpipe[2], status; size_t new_hash_len; pipe(inpipe); @@ -236,7 +237,6 @@ int main(int argc, char ** argv) { write(inpipe[1], password, password_len); close(inpipe[1]); - wait(&status); if(status) { fclose(creds); @@ -248,14 +248,38 @@ int main(int argc, char ** argv) { new_hash_len = read(outpipe[0], buf, 64 * 1024); close(outpipe[0]); - // Основывается на том, что последний символ вывода smtpctl encrypt - '\n' - buf[new_hash_len - 1] = '\0'; + if(new_hash_len == 0) { + fclose(creds); + fclose(creds_new); + remove("/etc/mail/creds.new"); + + return INVALID_INPUT; // Скорее всего, в этом виноват пользователь, либо серверу плохо + } + + buf[new_hash_len - 1] = '\0'; // Последний символ вывода smtpctl encrypt должен быть '\n' fputs(buf, creds_new); + // Копируем строку пользователя и весь оставшийся файл до конца fputc(':', creds_new); fputs(hash_iter + 1, creds_new); - copy_to(creds, creds_new, "/etc/mail/creds.new"); + if(!copy_to(creds, creds_new)) { + fclose(creds); + fclose(creds_new); + remove("/etc/mail/creds.new"); + + return INTERNAL_ERROR; + } + + + rewind(creds); + if(!backup(creds)) { + fclose(creds); + fclose(creds_new); + remove("/etc/mail/creds.new"); + + return INTERNAL_ERROR; + } fclose(creds_new); fclose(creds); diff --git a/index.html b/index.html index b67d27c..04871a9 100644 --- a/index.html +++ b/index.html @@ -60,7 +60,7 @@ return "Новые пароли не совпадают."; } if(response_body == "250") { - return "Неправильно заполнены поля, или слишком длинные данные (макс. 2048 байтов на поле)."; + return "Пустые, неправильно заполненные или слишком длинные поля (макс. 2048 байтов на поле)."; } return response_body; } diff --git a/webserver.go b/webserver.go index 876b6f5..96d4f18 100644 --- a/webserver.go +++ b/webserver.go @@ -38,88 +38,88 @@ var index_text []byte var passwd_file_mutex sync.Mutex func validate_chpasswd_input(reader io.Reader) ([]byte, bool) { - buf := make([]byte, 2049 * 4) - l, _ := reader.Read(buf) - - eof_check := make([]byte, 1) - n, err := reader.Read(eof_check) - if n != 0 || err != io.EOF { - return nil, false - } + buf := make([]byte, 2049 * 4) + l, _ := reader.Read(buf) - i := 0 - lines_found := 0 - line_start := 0 - for i < l { - if buf[i] == '\n' { - if(i + 1 - line_start > 2049 || i + 1 - line_start == 0) { - return nil, false - } + eof_check := make([]byte, 1) + n, err := reader.Read(eof_check) + if n != 0 || err != io.EOF { + return nil, false + } - line_start = i + 1 - lines_found++ - if(lines_found == 4) { - break - } + i := 0 + lines_found := 0 + line_start := 0 + for i < l { + if buf[i] == '\n' { + if(i + 1 - line_start > 2049 || i + 1 - line_start == 1) { + return nil, false + } + + line_start = i + 1 + lines_found++ + if(lines_found == 4) { + break + } } - i++ - } + i++ + } - if lines_found != 4 || i + 1 != l { - return nil, false - } - return buf, true + if lines_found != 4 || i + 1 != l { + return nil, false + } + return buf, true } func handle_request(writer http.ResponseWriter, request *http.Request) { - if request.URL.Path != "/" { - writer.WriteHeader(http.StatusNotFound) - writer.Write(status404_text) - } else { + if request.URL.Path != "/" { + writer.WriteHeader(http.StatusNotFound) + writer.Write(status404_text) + } else { if request.Method == http.MethodGet { - writer.Write(index_text) - } else if request.Method == http.MethodPost { - passwd_file_mutex.Lock() + writer.Write(index_text) + } else if request.Method == http.MethodPost { + passwd_file_mutex.Lock() - lines, could_read := validate_chpasswd_input(request.Body) - if !could_read { - writer.WriteHeader(http.StatusBadRequest) + lines, could_read := validate_chpasswd_input(request.Body) + if !could_read { + writer.WriteHeader(http.StatusBadRequest) writer.Write([]byte("250")) // -6, INVALID_INPUT passwd_file_mutex.Unlock() return - } + } - chpasswd := exec.Command("./chpasswd", "./chpasswd") - chpasswd.Stdin = strings.NewReader(string(lines)) - chpasswd_res := chpasswd.Run() - if chpasswd_res != nil { - writer.WriteHeader(http.StatusBadRequest) - writer.Write([]byte(strconv.Itoa(chpasswd_res.(*exec.ExitError).ExitCode()))) + chpasswd := exec.Command("./chpasswd", "./chpasswd") + chpasswd.Stdin = strings.NewReader(string(lines)) + chpasswd_res := chpasswd.Run() + if chpasswd_res != nil { + writer.WriteHeader(http.StatusBadRequest) + writer.Write([]byte(strconv.Itoa(chpasswd_res.(*exec.ExitError).ExitCode()))) - passwd_file_mutex.Unlock() + passwd_file_mutex.Unlock() return - } + } - update := exec.Command("/sbin/smtpctl", "update", "table", "creds") - update_res := update.Run() - if update_res != nil { - writer.WriteHeader(http.StatusBadRequest) - writer.Write([]byte("255")) // -1, INTERNAL_ERROR + update := exec.Command("/sbin/smtpctl", "update", "table", "creds") + update_res := update.Run() + if update_res != nil { + writer.WriteHeader(http.StatusBadRequest) + writer.Write([]byte("255")) // -1, INTERNAL_ERROR - passwd_file_mutex.Unlock() + passwd_file_mutex.Unlock() return - } + } - writer.Write([]byte("0")) + writer.Write([]byte("0")) - passwd_file_mutex.Unlock() - return + passwd_file_mutex.Unlock() + return } else { - writer.WriteHeader(http.StatusNotImplemented) + writer.WriteHeader(http.StatusNotImplemented) } - } + } } func handle_favicon(writer http.ResponseWriter, request *http.Request) { @@ -127,36 +127,36 @@ func handle_favicon(writer http.ResponseWriter, request *http.Request) { } func get_file_text(name string) []byte { - var err error + var err error - file, err := os.Open(name) - if err != nil { - log.Fatal(err) - } + file, err := os.Open(name) + if err != nil { + log.Fatal(err) + } - file_stat, err := file.Stat() - if err != nil { - file.Close() - log.Fatal(err) - } - text := make([]byte, file_stat.Size()) - - _, err = file.Read(text); - if err != nil { - file.Close() - log.Fatal(err) - } + file_stat, err := file.Stat() + if err != nil { + file.Close() + log.Fatal(err) + } + text := make([]byte, file_stat.Size()) - file.Close() - return text + _, err = file.Read(text); + if err != nil { + file.Close() + log.Fatal(err) + } + + file.Close() + return text } func main() { - index_text = get_file_text("index.html") - status404_text = get_file_text("404.html") + index_text = get_file_text("index.html") + status404_text = get_file_text("404.html") - http.HandleFunc("/favicon.ico", handle_favicon) - http.HandleFunc("/", handle_request) - http.ListenAndServe(":62272", nil) + http.HandleFunc("/favicon.ico", handle_favicon) + http.HandleFunc("/", handle_request) + http.ListenAndServe(":62272", nil) }