init(web): add http server on golang with authentication only
And remove CGI
This commit is contained in:
parent
e4188c69a4
commit
7f31d62260
@ -28,7 +28,3 @@ add_library(tanabata SHARED ${TANABATA_SRC})
|
|||||||
|
|
||||||
# Tanabata CLI app
|
# Tanabata CLI app
|
||||||
add_executable(tfm ${TANABATA_SRC} ${CLI_SRC})
|
add_executable(tfm ${TANABATA_SRC} ${CLI_SRC})
|
||||||
|
|
||||||
# Authentication CGI app
|
|
||||||
add_executable(tfm-cgi ${TANABATA_SRC} cgi/cgi.c)
|
|
||||||
target_link_libraries(tfm-cgi fcgi ssl crypto pthread)
|
|
||||||
|
|||||||
114
cgi/cgi.c
114
cgi/cgi.c
@ -1,114 +0,0 @@
|
|||||||
#include <fcgi_stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#define TOKEN_RENTALTIME 604800
|
|
||||||
#define TOKEN_SIZE 64
|
|
||||||
|
|
||||||
static time_t SID;
|
|
||||||
static char TOKEN[TOKEN_SIZE];
|
|
||||||
static int socket_auth;
|
|
||||||
static int socket_cgi;
|
|
||||||
|
|
||||||
int validate(FCGX_Request *request) {
|
|
||||||
if (time(NULL) - SID > TOKEN_RENTALTIME) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
char token[TOKEN_SIZE];
|
|
||||||
FCGX_GetStr(token, TOKEN_SIZE, request->in);
|
|
||||||
if (memcmp(token, TOKEN, TOKEN_SIZE) == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *auth() {
|
|
||||||
FCGX_Request request;
|
|
||||||
if (FCGX_InitRequest(&request, socket_auth, FCGI_FAIL_ACCEPT_ON_INTR) != 0) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
unsigned char password[SHA256_DIGEST_LENGTH];
|
|
||||||
FILE *passfile = fopen("/etc/tfm/password", "rb");
|
|
||||||
if (passfile == NULL ||
|
|
||||||
fread(password, 1, SHA256_DIGEST_LENGTH, passfile) < SHA256_DIGEST_LENGTH) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
fclose(passfile);
|
|
||||||
int rc;
|
|
||||||
char buffer[33];
|
|
||||||
for (;;) {
|
|
||||||
static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
pthread_mutex_lock(&accept_mutex);
|
|
||||||
rc = FCGX_Accept_r(&request);
|
|
||||||
pthread_mutex_unlock(&accept_mutex);
|
|
||||||
if (rc < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
memset(buffer, 0, 33);
|
|
||||||
FCGX_GetStr(buffer, 32, request.in);
|
|
||||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
|
||||||
SHA256((const unsigned char *) buffer, strlen(buffer), hash);
|
|
||||||
if (memcmp(hash, password, SHA256_DIGEST_LENGTH) == 0) {
|
|
||||||
time(&SID);
|
|
||||||
uint64_t subtoken = SID;
|
|
||||||
SHA256((const unsigned char *) &subtoken, 8, hash);
|
|
||||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
|
||||||
sprintf(TOKEN + (i * 2), "%02x", hash[i]);
|
|
||||||
}
|
|
||||||
FCGX_PutS("Content-type: application/json\r\n\r\n"
|
|
||||||
"{\"status\":true,\"token\":\"", request.out);
|
|
||||||
FCGX_PutS(TOKEN, request.out);
|
|
||||||
FCGX_PutS("\"}\n", request.out);
|
|
||||||
FCGX_Finish_r(&request);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
FCGX_PutS("Content-type: application/json\r\n\r\n"
|
|
||||||
"{\"status\":false}\n", request.out);
|
|
||||||
FCGX_Finish_r(&request);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *tfmcgi() {
|
|
||||||
FCGX_Request request;
|
|
||||||
if (FCGX_InitRequest(&request, socket_cgi, FCGI_FAIL_ACCEPT_ON_INTR) != 0) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
int rc;
|
|
||||||
for (;;) {
|
|
||||||
static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
pthread_mutex_lock(&accept_mutex);
|
|
||||||
rc = FCGX_Accept_r(&request);
|
|
||||||
pthread_mutex_unlock(&accept_mutex);
|
|
||||||
if (rc < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (validate(&request) != 0) {
|
|
||||||
FCGX_PutS("Content-type: application/json\r\n\r\n"
|
|
||||||
"{\"status\":false}\n", request.out);
|
|
||||||
FCGX_Finish_r(&request);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
FCGX_PutS("Content-type: application/json\r\n\r\n"
|
|
||||||
"{\"status\":true}\n", request.out);
|
|
||||||
FCGX_Finish_r(&request);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
pthread_t thread_auth, thread_cgi;
|
|
||||||
FCGX_Init();
|
|
||||||
if ((socket_auth = FCGX_OpenSocket("/tmp/tfm-auth.sock", 0)) == -1 ||
|
|
||||||
(socket_cgi = FCGX_OpenSocket("/tmp/tfm-cgi.sock", 0)) == -1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
pthread_create(&thread_auth, NULL, auth, NULL);
|
|
||||||
pthread_create(&thread_cgi, NULL, tfmcgi, NULL);
|
|
||||||
pthread_join(thread_auth, NULL);
|
|
||||||
pthread_join(thread_cgi, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
145
web/web.go
Normal file
145
web/web.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSON struct {
|
||||||
|
Status bool `json:"status,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const TOKEN_VALIDTIME = 604800
|
||||||
|
|
||||||
|
var SID int64 = 0
|
||||||
|
var TOKEN = ""
|
||||||
|
|
||||||
|
func TokenGenerate(seed []byte) {
|
||||||
|
SID = time.Now().Unix()
|
||||||
|
value := SID
|
||||||
|
for _, char := range seed {
|
||||||
|
value += int64(char)
|
||||||
|
}
|
||||||
|
TOKEN = fmt.Sprintf("%x", sha256.Sum256([]byte(strconv.FormatInt(value, 16))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenValidate(token string) bool {
|
||||||
|
if time.Now().Unix()-SID >= TOKEN_VALIDTIME || token != TOKEN {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandlerAuth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var buffer = make([]byte, sha256.Size)
|
||||||
|
var response = JSON{Status: false}
|
||||||
|
var passhash = make([]byte, sha256.Size)
|
||||||
|
var hash [sha256.Size]byte
|
||||||
|
var passlen = sha256.Size
|
||||||
|
var err error
|
||||||
|
passfile, err := os.Open("/etc/tfm/password")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to open password file: %s\n", err)
|
||||||
|
}
|
||||||
|
read, err := passfile.Read(passhash)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to read password file: %s\n", err)
|
||||||
|
}
|
||||||
|
if read != sha256.Size {
|
||||||
|
log.Fatalln("Invalid password file")
|
||||||
|
}
|
||||||
|
err = passfile.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to close password file: %s\n", err)
|
||||||
|
}
|
||||||
|
_, err = r.Body.Read(buffer)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < sha256.Size; i++ {
|
||||||
|
if buffer[i] == 0 {
|
||||||
|
passlen = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash = sha256.Sum256(buffer[:passlen])
|
||||||
|
if bytes.Equal(hash[:], passhash) {
|
||||||
|
TokenGenerate(buffer)
|
||||||
|
response.Status = true
|
||||||
|
response.Token = TOKEN
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
jsonData, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
_, err = w.Write(jsonData)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandlerTFM(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var request JSON
|
||||||
|
var response = JSON{Status: false}
|
||||||
|
var err error
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1048576)
|
||||||
|
json_decoder := json.NewDecoder(r.Body)
|
||||||
|
json_decoder.DisallowUnknownFields()
|
||||||
|
err = json_decoder.Decode(&request)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if TokenValidate(request.Token) {
|
||||||
|
response.Status = true
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
jsonData, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
_, err = w.Write(jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Println("Initializing...")
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: ":42776",
|
||||||
|
}
|
||||||
|
public_fs := http.FileServer(http.Dir("/var/www/tfm"))
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path != "/" && path.Ext(r.URL.Path) == "" {
|
||||||
|
r.URL.Path += ".html"
|
||||||
|
}
|
||||||
|
public_fs.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
http.HandleFunc("/AUTH", HandlerAuth)
|
||||||
|
http.HandleFunc("/TFM", HandlerTFM)
|
||||||
|
tfm_fs := http.FileServer(http.Dir("/srv/data/tfm"))
|
||||||
|
http.Handle("/static", tfm_fs)
|
||||||
|
log.Println("Running...")
|
||||||
|
err := server.ListenAndServeTLS("/etc/ssl/certs/tfm.crt", "/etc/ssl/private/tfm.key")
|
||||||
|
if errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Fatalln("Server closed")
|
||||||
|
} else if err != nil {
|
||||||
|
log.Fatalf("Error starting server: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user