2025-01-08 12:11:26 +00:00
|
|
|
|
// Веб-сервер для смены пароля на почтовый ящик @sch9.ru
|
|
|
|
|
//
|
|
|
|
|
// Copyright (c) 2025 Nikita Osokin.
|
|
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
|
|
|
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
|
|
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
|
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
|
|
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"io"
|
|
|
|
|
"log"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var status404_text []byte
|
|
|
|
|
var index_text []byte
|
|
|
|
|
var passwd_file_mutex sync.Mutex
|
|
|
|
|
|
|
|
|
|
func validate_chpasswd_input(reader io.Reader) ([]byte, bool) {
|
2025-01-14 18:46:12 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
2025-01-08 12:11:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
i++
|
|
|
|
|
}
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
if lines_found != 4 || i + 1 != l {
|
|
|
|
|
return nil, false
|
|
|
|
|
}
|
|
|
|
|
return buf, true
|
2025-01-08 12:11:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handle_request(writer http.ResponseWriter, request *http.Request) {
|
2025-01-14 18:46:12 +00:00
|
|
|
|
if request.URL.Path != "/" {
|
|
|
|
|
writer.WriteHeader(http.StatusNotFound)
|
|
|
|
|
writer.Write(status404_text)
|
|
|
|
|
} else {
|
2025-01-08 12:11:26 +00:00
|
|
|
|
if request.Method == http.MethodGet {
|
2025-01-14 18:46:12 +00:00
|
|
|
|
writer.Write(index_text)
|
|
|
|
|
} else if request.Method == http.MethodPost {
|
|
|
|
|
passwd_file_mutex.Lock()
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
lines, could_read := validate_chpasswd_input(request.Body)
|
|
|
|
|
if !could_read {
|
|
|
|
|
writer.WriteHeader(http.StatusBadRequest)
|
2025-01-08 12:11:26 +00:00
|
|
|
|
writer.Write([]byte("250")) // -6, INVALID_INPUT
|
|
|
|
|
|
|
|
|
|
passwd_file_mutex.Unlock()
|
|
|
|
|
return
|
2025-01-14 18:46:12 +00:00
|
|
|
|
}
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
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())))
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
passwd_file_mutex.Unlock()
|
2025-01-08 12:11:26 +00:00
|
|
|
|
return
|
2025-01-14 18:46:12 +00:00
|
|
|
|
}
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
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
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
passwd_file_mutex.Unlock()
|
2025-01-08 12:11:26 +00:00
|
|
|
|
return
|
2025-01-14 18:46:12 +00:00
|
|
|
|
}
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
writer.Write([]byte("0"))
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
passwd_file_mutex.Unlock()
|
|
|
|
|
return
|
2025-01-08 12:11:26 +00:00
|
|
|
|
} else {
|
2025-01-14 18:46:12 +00:00
|
|
|
|
writer.WriteHeader(http.StatusNotImplemented)
|
2025-01-08 12:11:26 +00:00
|
|
|
|
}
|
2025-01-14 18:46:12 +00:00
|
|
|
|
}
|
2025-01-08 12:11:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handle_favicon(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
|
writer.WriteHeader(http.StatusNotFound)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func get_file_text(name string) []byte {
|
2025-01-14 18:46:12 +00:00
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
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.Close()
|
|
|
|
|
return text
|
2025-01-08 12:11:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
2025-01-14 18:46:12 +00:00
|
|
|
|
index_text = get_file_text("index.html")
|
|
|
|
|
status404_text = get_file_text("404.html")
|
2025-01-08 12:11:26 +00:00
|
|
|
|
|
2025-01-14 18:46:12 +00:00
|
|
|
|
http.HandleFunc("/favicon.ico", handle_favicon)
|
|
|
|
|
http.HandleFunc("/", handle_request)
|
|
|
|
|
http.ListenAndServe(":62272", nil)
|
2025-01-08 12:11:26 +00:00
|
|
|
|
}
|
|
|
|
|
|