Compare commits

..

No commits in common. "master" and "v0.3.1" have entirely different histories.

13 changed files with 241 additions and 471 deletions

View File

@ -1,70 +1,52 @@
# HTTPAuthenticationOverXMPP # HTTPAuthentificationOverXMPP
Provide an HTTP authentication over XMPP. Implementation of [XEP-0070](https://xmpp.org/extensions/xep-0070.html). Provide an HTTP anthentification over XMPP. Implementation of [XEP-0070](https://xmpp.org/extensions/xep-0070.html).
Can be run as a XMPP client or XMPP component.
## Compilation
### Dependencies ### Dependencies
* [go-xmpp](https://git.kingpenguin.tk/chteufleur/go-xmpp) for the XMPP part. * [go-xmpp](https://git.kingpenguin.tk/chteufleur/go-xmpp) for the XMPP part.
* [cfg](https://github.com/jimlawless/cfg) for the configuration file. * [cfg](https://github.com/jimlawless/cfg) for the configuration file.
### Build and run
You must first [install go environment](https://golang.org/doc/install) on your system. You must first [install go environment](https://golang.org/doc/install) on your system.
Then, go into your $GOPATH directory and go get the source code. Then, go into your $GOPATH directory and go get the source code.
```sh ```sh
go get git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP.git go get git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP.git
``` ```
First, you need to go into directory ``$GOPATH/src/chteufleur/HTTPAuthentificationOverXMPP.git``.
Then, you can run the project directly by using command ``go run main.go``.
Or, in order to build the project you can run the command ``go build main.go``.
It will generate a binary that you can run as any binary file.
### Configure ### Configure
Configure the gateway by editing the ``httpAuth.conf`` file in order to give all XMPP and HTTP server informations. This configuration file has to be placed following the [XDG specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) (example ``/etc/xdg/http-auth/httpAuth.conf``). Configure the gateway by editing the ``httpAuth.cfg`` file in order to give all XMPP component and HTTP server informations.
An example of the config file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/httpAuth.conf). An example of the config file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/httpAuth.cfg).
XMPP XMPP
* xmpp_server_address : Component server address connection (default: 127.0.0.1) * xmpp_server_address : Component server address connection (default: 127.0.0.1)
* xmpp_server_port : Component server port connection (default: 5347) * xmpp_server_port : Component server port connection (default: 5347)
* __xmpp_jid__ : Account JID * xmpp_hostname : Component hostname
* __xmpp_secret__ : Account password * xmpp_secret : Component password
* xmpp_debug : Enable debug log at true (default: false) * xmpp_debug : Enable debug log at true (default: false)
* xmpp_verify_cert_validity : Enable certificate verification (default: true)
* xmpp_default_lang : Message default languages
HTTP HTTP
* http_port : HTTP port to bind (default: -1, desactive: -1) * http_port : HTTP port to bind (default: 9090, desactive: -1)
* https_port : HTTPS port to bind (default: -1, desactive: -1) * https_port : HTTPS port to bind (default: 9093, desactive: -1)
* https_cert_path : Path to the certificate file (default: ./cert.pem) * https_cert_path : Path to the certificate file (default: ./cert.pem)
* https_key_path : Path to the key file (default: ./key.pem) * https_key_path : Path to the key file (default: ./key.pem)
* http_timeout_sec : Define a timeout if user did not give an answer to the request (default: 60) * http_timeoute_sec : Define a timeout if user did not give an answer to the request (default: 60)
* http_bind_address_ipv4 : Bind address on IPv4 (default: 127.0.0.1)
* http_bind_address_ipv6 : Bind address on IPv6 (default: [::1])
__Bold config__ are mandatory.
If ``http_bind_address_ipv4`` is set to ``0.0.0.0``, it will bind all address on IPv4 __AND__ IPv6. ### Utilization
The lang messages file must be placed into the same directory than the configuration file.
An example of this file can be found in [the repos](https://git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP/src/master/messages.lang)
### Usage
To ask authorization, just send an HTTP request to the path ``/auth`` with parameters: To ask authorization, just send an HTTP request to the path ``/auth`` with parameters:
* __jid__ : JID of the user (user@host/resource or user@host) * __jid__ : JID of the user (user@host/resource or user@host)
* __domain__ : Domain you want to access * __domain__ : Domain you want to access
* __method__ : Method you access the domain * __method__ : Method you access the domain
* __transaction_id__ : Transaction identifier (auto generated if not provide) * transaction_id : Transaction identifier (auto generated if not provide)
* timeout : Timeout of the request in second (default : 60, max : 300) * timeout : Timeout of the request in second (default : 60, max : 300)
__Bold parameters__ are mandatory. __Bold parameters__ are mandatory.
Example: Example:
``` ```
GET /auth?jid=user%40host%2fresource;domain=example.org;method=POST;transaction_id=WhatEverYouWant;timeout=120 HTTP/1.1 GET /auth?jid=user%40host%2fresource&domain=example.org&method=POST&transaction_id=WhatEverYouWant&timeout=120 HTTP/1.1
``` ```
This will send a request to the given JID, then return HTTP code depending on what appended. This will send a request to the given JID, then return HTTP code depending on what appended.

22
cert.pem Normal file
View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDmjCCAoICCQDTJ1wt8ibb0DANBgkqhkiG9w0BAQsFADCBjjELMAkGA1UEBhMC
RlIxEzARBgNVBAgMClNvbWUtU3RhdGUxEDAOBgNVBAcMB1ZhbGVuY2UxITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5v
cmcxHzAdBgkqhkiG9w0BCQEWEHRvdG9AZXhhbXBsZS5vcmcwHhcNMTYwNzE0MDgx
MDA3WhcNMTcwNzE0MDgxMDA3WjCBjjELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNv
bWUtU3RhdGUxEDAOBgNVBAcMB1ZhbGVuY2UxITAfBgNVBAoMGEludGVybmV0IFdp
ZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B
CQEWEHRvdG9AZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCjNtyD6vXdVSj+Vmin/1t4JApafQ4475oJsvsNfd2cMgQ9856RZPyZFCCe
9veffUSV9ffYcgtPF8ZfRkOLZvSzYNYrrgI+Qsp2Y/Mw1hAupn2IadjdB0ZAFpZi
fnH5tuXSIiPbrl1sQZxSdIhgRPdj6scnBFwjbbm+DfyQYvjtm5LTOYbNOc/Sali+
lSdC22Z69nL7rscg3LFeBb8Oqx6MU8cvQ/nNsfxP+Ynimon3E28mP8VftyL81J6z
g8H6ly9R3kkapPrUg3CWP1z5rya+MdujcxkhEfO9oybSokaU/VkpMfrM4iBjs9m5
+hU3kWfVqB38OYUVEz2GagWrvDdJAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD3/
fkCOvAhy38R10IgYEVH8MlrG3Rpy7CCYhUPgBrdFRgFAhwegDSc5my4rgiGmETkX
SlEppuHyoMNe8Sv70ibflqrCcwMPz/BWRlTDSWdwUqNjr4RfOT6LrO/9bZQT4tV8
Av4smslUaXiKuFyJLY1uFXbw2BqZTQQgaDQGdaqRNkCPRT00+VUbzR/qOEQ0Kv0U
QAX1tN1kmrJo5ccGB9WVvijp1ZlimMjtvh5v7Uxa6Wl4l7D9B1+5glEhPKunOlcK
MAHrHIuBRuuQUmtdk9zj3HYZtniChflPUO3+QWr3nfA6IhVEvz1wa4BQFzfwm4NY
eFcBWz6pr3zuawKjUlk=
-----END CERTIFICATE-----

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"log" "log"
"math/rand"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -29,31 +28,23 @@ const (
RETURN_VALUE_OK = "OK" RETURN_VALUE_OK = "OK"
RETURN_VALUE_NOK = "NOK" RETURN_VALUE_NOK = "NOK"
MAX_PORT_VAL = 65535
StatusUnknownError = 520 StatusUnknownError = 520
StatusUnreachable = 523 StatusUnreachable = 523
) )
var ( var (
HttpPortBind = -1 HttpPortBind = 9090
HttpsPortBind = -1 HttpsPortBind = 9093
CertPath = "./cert.pem" CertPath = "./cert.pem"
KeyPath = "./key.pem" KeyPath = "./key.pem"
ChanRequest = make(chan interface{}, 5) ChanRequest = make(chan interface{}, 5)
TimeoutSec = 60 // 1 min TimeoutSec = 60 // 1 min
MaxTimeout = 300 // 5 min MaxTimeout = 300 // 5 min
BindAddressIPv4 = "127.0.0.1"
BindAddressIPv6 = "[::1]"
) )
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
func indexHandler(w http.ResponseWriter, r *http.Request) { func indexHandler(w http.ResponseWriter, r *http.Request) {
// TODO
fmt.Fprintf(w, "Welcome to HTTP authentification over XMPP") fmt.Fprintf(w, "Welcome to HTTP authentification over XMPP")
} }
@ -62,15 +53,15 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
jid := strings.Join(r.Form[PARAM_JID], "") jid := strings.Join(r.Form[PARAM_JID], "")
method := strings.Join(r.Form[METHOD_ACCESS], "") method := strings.Join(r.Form[METHOD_ACCESS], "")
domain := strings.Join(r.Form[DOMAIN_ACCESS], "") domain := strings.Join(r.Form[DOMAIN_ACCESS], "")
transaction := strings.Join(r.Form[TRANSACTION_ID], "")
if jid == "" || method == "" || domain == "" || transaction == "" { if jid == "" || method == "" || domain == "" {
// If mandatory params is missing // If mandatory params is missing
log.Printf("%sMandatory params is missing", LogInfo) log.Printf("%sMandatory params is missing", LogInfo)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
transaction := strings.Join(r.Form[TRANSACTION_ID], "")
timeoutStr := strings.Join(r.Form[TIMEOUTE], "") timeoutStr := strings.Join(r.Form[TIMEOUTE], "")
log.Printf("%sAuth %s", LogInfo, jid) log.Printf("%sAuth %s", LogInfo, jid)
timeout, err := strconv.Atoi(timeoutStr) timeout, err := strconv.Atoi(timeoutStr)
@ -83,13 +74,13 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
chanAnswer := make(chan string) chanAnswer := make(chan string)
confirmation := new(xmpp.Confirmation) client := new(xmpp.Client)
confirmation.JID = jid client.JID = jid
confirmation.Method = method client.Method = method
confirmation.Domain = domain client.Domain = domain
confirmation.Transaction = transaction client.Transaction = transaction
confirmation.ChanReply = chanAnswer client.ChanReply = chanAnswer
confirmation.SendConfirmation() client.QueryClient()
select { select {
case answer := <-chanAnswer: case answer := <-chanAnswer:
@ -110,14 +101,14 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
} }
switch confirmation.TypeSend { switch client.TypeSend {
case xmpp.TYPE_SEND_IQ: case xmpp.TYPE_SEND_IQ:
log.Printf("%sDelete IQ", LogDebug) log.Printf("%sDelete IQ", LogDebug)
delete(xmpp.WaitIqMessages, confirmation.IdMap) delete(xmpp.WaitIqMessages, client.IdMap)
case xmpp.TYPE_SEND_MESSAGE: case xmpp.TYPE_SEND_MESSAGE:
log.Printf("%sDelete Message", LogDebug) log.Printf("%sDelete Message", LogDebug)
delete(xmpp.WaitMessageAnswers, confirmation.IdMap) delete(xmpp.WaitMessageAnswers, client.IdMap)
} }
} }
@ -128,44 +119,26 @@ func Run() {
http.HandleFunc(ROUTE_AUTH, authHandler) http.HandleFunc(ROUTE_AUTH, authHandler)
if HttpPortBind > 0 { if HttpPortBind > 0 {
go runHttp(BindAddressIPv4) go runHttp()
if BindAddressIPv4 != "0.0.0.0" {
go runHttp(BindAddressIPv6)
}
} else if HttpPortBind == 0 {
HttpPortBind = rand.Intn(MAX_PORT_VAL)
go runHttp(BindAddressIPv4)
if BindAddressIPv4 != "0.0.0.0" {
go runHttp(BindAddressIPv6)
}
} }
if HttpsPortBind > 0 { if HttpsPortBind > 0 {
go runHttps(BindAddressIPv4) go runHttps()
if BindAddressIPv6 != "0.0.0.0" {
go runHttps(BindAddressIPv6)
}
} else if HttpsPortBind == 0 {
HttpsPortBind = rand.Intn(MAX_PORT_VAL)
go runHttps(BindAddressIPv4)
if BindAddressIPv6 != "0.0.0.0" {
go runHttps(BindAddressIPv6)
}
} }
} }
func runHttp(bindAddress string) { func runHttp() {
port := strconv.Itoa(HttpPortBind) port := strconv.Itoa(HttpPortBind)
log.Printf("%sHTTP listenning on %s:%s", LogInfo, bindAddress, port) log.Printf("%sHTTP listenning on port %s", LogInfo, port)
err := http.ListenAndServe(bindAddress+":"+port, nil) err := http.ListenAndServe(":"+port, nil)
if err != nil { if err != nil {
log.Fatal("%sListenAndServe: ", LogError, err) log.Fatal("%sListenAndServe: ", LogError, err)
} }
} }
func runHttps(bindAddress string) { func runHttps() {
port := strconv.Itoa(HttpsPortBind) port := strconv.Itoa(HttpsPortBind)
log.Printf("%sHTTPS listenning on %s:%s", LogInfo, bindAddress, port) log.Printf("%sHTTPS listenning on port %s", LogInfo, port)
err := http.ListenAndServeTLS(bindAddress+":"+port, CertPath, KeyPath, nil) err := http.ListenAndServeTLS(":"+port, CertPath, KeyPath, nil)
if err != nil { if err != nil {
log.Fatal("%sListenAndServe: ", LogError, err) log.Fatal("%sListenAndServe: ", LogError, err)
} }

View File

@ -1,16 +1,13 @@
# XMPP informations (component) # XMPP informations
xmpp_server_address=192.168.1.2 xmpp_server_address=192.168.1.2
xmpp_server_port=5347 xmpp_server_port=5347
xmpp_jid=xmppsteam.kingpenguin.tk xmpp_hostname=xmppsteam.kingpenguin.tk
xmpp_secret=xmpp4steam_password xmpp_secret=xmpp4steam_password
xmpp_debug=true xmpp_debug=true
xmpp_verify_cert_validity=true
# HTTP informations # HTTP informations
http_bind_address_ipv4=127.0.0.1
http_bind_address_ipv6=[::1]
http_port=9090 http_port=9090
https_port=9093 https_port=9093
https_cert_path=./cert.pem https_cert_path=./cert.pem
https_key_path=./key.pem https_key_path=./key.pem
http_timeout_sec=60 http_timeoute_sec=60

27
key.pem Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAozbcg+r13VUo/lZop/9beCQKWn0OOO+aCbL7DX3dnDIEPfOe
kWT8mRQgnvb3n31ElfX32HILTxfGX0ZDi2b0s2DWK64CPkLKdmPzMNYQLqZ9iGnY
3QdGQBaWYn5x+bbl0iIj265dbEGcUnSIYET3Y+rHJwRcI225vg38kGL47ZuS0zmG
zTnP0mpYvpUnQttmevZy+67HINyxXgW/DqsejFPHL0P5zbH8T/mJ4pqJ9xNvJj/F
X7ci/NSes4PB+pcvUd5JGqT61INwlj9c+a8mvjHbo3MZIRHzvaMm0qJGlP1ZKTH6
zOIgY7PZufoVN5Fn1agd/DmFFRM9hmoFq7w3SQIDAQABAoIBAQCAI6lbVJP1Uk/d
5v9BrkUk/L64LmiFIPAB32glPoVHhSk5blQ2+F8s29WEmIbuy42WYsdUQq1ISnUv
Bd4vywQg9M0Q/Au8z/lem7gpxlZsGcCC4f8mAPkRhepJp9ZZ5FNo9+7JIYstXBGb
1uvfESZdZs02f8DK+/GRGjAJN/sRqAo4MvwZzrX9DIMnTt5MiujCAWOMX4rXT316
epSyUAxMnNzC3On32TmGqrHXTF7KMGPjbSupAHDS4F8iL9ntFF2QPcW5um8jEneZ
ln/oJo2+2LYFJPcj0BbvCAcRaIumvHNQgfC0ispStCR37IlEZKyQXUtX/z/BPQO/
d8KzylqRAoGBANl1tMVz4d20kkQzyXOCmQ7C/y98AHWDiFH2Iq/QMV21TFomit66
SR8RVwWH3G6C8cQRHFa504iDP3Lo/jq+rf4SMwEJq+X5aVkThCq5DDZech/qslyF
FTA4IIkcqxF5/UOQg3UxGKU2I7mKLLgnhaPvPqcd7t/PzXAgJqV45ElFAoGBAMAk
AHPZFpb5IMPbVSen/jZ6cbNtRsOyUv2Vlz+Pkjbfl0SsxOInPZxwwEcEgLC2micN
CH1rTZXpMLKOr/VBdkbp4uMMR8X2kro02b9zlmBmAXleyuAPbQAbRn4epnnfzqxb
TcJNHQJT4uQRhtshTQ2GqnHXei/ZMBrI35VCN5w1AoGBAM9c3LqE3Fbrv6Zls64A
VS+sZmbDWjS07qMpkL4SS2DOZzZ4Fmh5PwzvHgpaGasQFrcekeVpYfuFHFXZM8SU
25mxhQ1ySYcNJJYadCfBOZIG0dD5nod3KFNI0k2tFrudlhJ9lb2EybmRPNPKnQYm
OduvYhE+C/FEWOSY5AFanGX5AoGARxWKrVFlUBl/C7a7fF5kaFdIdW86PPBeT77m
I/fDylVSK3AXruuBmb0FBcEes0H7KfNibrQiEhIhmA29/2hmj7m73PAQJachhY5D
+NaUjblvVi3BtL9APkfY/pPsVy570bw9umK5FsFeMa5iS/O4BAcMS+3CIK2jZGVo
glnrJPkCgYEAiI0nFwbd5oP4bP41zDKU7lrnwrmbCKZr5ZYScw1NtSqKo7aJnhEi
fBYMT8aXHVt4ALcI/VzR8LnoTQcJ6Pibn0pZ91sE8FRk99qfnF0rK4LLKF6R+UJH
X+dmoMOcHQ9MEyPhNvdgsTo72KMPzJn8q58VTq/6I4we+VCOlZNwlCA=
-----END RSA PRIVATE KEY-----

84
main.go
View File

@ -10,17 +10,16 @@ import (
"os" "os"
"os/signal" "os/signal"
"strconv" "strconv"
"strings"
"syscall" "syscall"
"time" "time"
) )
const ( const (
Version = "v0.5-dev" Version = "v0.3.1"
configurationFilePath = "http-auth/httpAuth.conf" configurationFilePath = "httpAuth.cfg"
langFilePath = "http-auth/messages.lang"
PathConfEnvVariable = "XDG_CONFIG_DIRS" default_xmpp_server_address = "127.0.0.1"
DefaultXdgConfigDirs = "/etc/xdg" default_xmpp_server_port = "5347"
) )
var ( var (
@ -30,13 +29,13 @@ var (
func init() { func init() {
log.Printf("Running HTTP-Auth %v", Version) log.Printf("Running HTTP-Auth %v", Version)
if !loadConfigFile() { err := cfg.Load(configurationFilePath, mapConfig)
log.Fatal("Failed to load configuration file.") if err != nil {
log.Fatal("Failed to load configuration file.", err)
} }
loadLangFile()
// HTTP config // HTTP config
httpTimeout, err := strconv.Atoi(mapConfig["http_timeout_sec"]) httpTimeout, err := strconv.Atoi(mapConfig["http_timeoute_sec"])
if err == nil && httpTimeout > 0 && httpTimeout < http.MaxTimeout { if err == nil && httpTimeout > 0 && httpTimeout < http.MaxTimeout {
log.Println("Define HTTP timeout to " + strconv.Itoa(httpTimeout) + " second") log.Println("Define HTTP timeout to " + strconv.Itoa(httpTimeout) + " second")
http.TimeoutSec = httpTimeout http.TimeoutSec = httpTimeout
@ -53,71 +52,28 @@ func init() {
http.CertPath = mapConfig["https_cert_path"] http.CertPath = mapConfig["https_cert_path"]
http.KeyPath = mapConfig["https_key_path"] http.KeyPath = mapConfig["https_key_path"]
} }
bindAddressIPv4 := mapConfig["http_bind_address_ipv4"]
if bindAddressIPv4 != "" {
http.BindAddressIPv4 = bindAddressIPv4
}
bindAddressIPv6 := mapConfig["http_bind_address_ipv6"]
if bindAddressIPv6 != "" {
http.BindAddressIPv6 = bindAddressIPv6
}
// XMPP config // XMPP config
xmpp_server_address := mapConfig["xmpp_server_address"] xmpp_server_address := mapConfig["xmpp_server_address"]
if xmpp_server_address != "" { if xmpp_server_address == "" {
xmpp.Addr = xmpp_server_address xmpp_server_address = default_xmpp_server_address
} }
xmpp_server_port := mapConfig["xmpp_server_port"] xmpp_server_port := mapConfig["xmpp_server_port"]
if xmpp_server_port != "" { if xmpp_server_port == "" {
xmpp.Port = xmpp_server_port xmpp_server_port = default_xmpp_server_port
} }
xmpp.JidStr = mapConfig["xmpp_jid"] xmpp.Addr = xmpp_server_address + ":" + xmpp_server_port
xmpp.JidStr = mapConfig["xmpp_hostname"]
xmpp.Secret = mapConfig["xmpp_secret"] xmpp.Secret = mapConfig["xmpp_secret"]
xmpp.Debug = mapConfig["xmpp_debug"] == "true" xmpp.Debug = mapConfig["xmpp_debug"] == "true"
xmpp.VerifyCertValidity = mapConfig["xmpp_verify_cert_validity"] != "false" // Default TRUE
} }
func loadConfigFile() bool { func getChanString(c chan interface{}) string {
ret := false ret := ""
envVariable := os.Getenv(PathConfEnvVariable) i := <-c
if envVariable == "" { if v, ok := i.(string); ok {
envVariable = DefaultXdgConfigDirs ret = v
}
for _, path := range strings.Split(envVariable, ":") {
log.Println("Try to find configuration file into " + path)
configFile := path + "/" + configurationFilePath
if _, err := os.Stat(configFile); err == nil {
// The config file exist
if cfg.Load(configFile, mapConfig) == nil {
// And has been loaded succesfully
log.Println("Find configuration file at " + configFile)
ret = true
break
}
}
}
return ret
}
func loadLangFile() bool {
ret := false
envVariable := os.Getenv(PathConfEnvVariable)
if envVariable == "" {
envVariable = DefaultXdgConfigDirs
}
for _, path := range strings.Split(envVariable, ":") {
log.Println("Try to find messages lang file into " + path)
langFile := path + "/" + langFilePath
if _, err := os.Stat(langFile); err == nil {
// The config file exist
if cfg.Load(langFile, xmpp.MapLangs) == nil {
// And has been loaded succesfully
log.Println("Find messages lang file at " + langFile)
ret = true
break
}
}
} }
return ret return ret
} }

View File

@ -1,4 +0,0 @@
en=_DOMAIN_ (with method _METHOD_) need to validate your identity, do you agree?\nValidation code: _VALIDE_CODE_\nPlease check that this code is the same as on _DOMAIN_.\n\nIf your client doesn't support that functionality, please send back the validation code to confirm the request.
fr=_DOMAIN_ (avec la methode _METHOD_) a besoin de valider votre identitée, acceptez-vous ?\nCode de validation : _VALIDE_CODE_\nVeuillez vérifier que ce code est le même que sur _DOMAIN_.\n\nSi votre client ne support pas cette fonctionnalitée, veuilez renvoyer le code de validation pour confirmer la requête.
ca=_DOMAIN_ (via _METHOD_) validarà la teva identitat, hi estàs d'acord?\nCodi de validació: _VALIDE_CODE_ \nComprova que aquest codi sigui el mateix que a _DOMAIN_.\n\nSi la teva aplicació no contempla aquesta funcionalitat, respon a aquest missatge amb el codi per a confirmar la petició.
es=_DOMAIN_ (via _METHOD_) validará tu identitad, estás de acuerdo?\nCódigo de validación: _VALIDE_CODE_ \nComprueba que el código sea el mismo que en _DOMAIN_.\n\nSi tu aplicación no contempla esta funcionalidad, responde a este mensaje con el código para confirmar la petición.

View File

@ -1,40 +0,0 @@
# Packaging
## Build libraries as shared
In order to packaged the project, you will need to follow those different steps.
We will build libraries as shared libraries.
The first step is to buid the GO standard library as shared.
```
$ cd $GOPATH
$ go install -buildmode=shared std
```
Next, we will build the 2 libraries to make them shared…
```
$ cd $GOPATH
$ go install -buildmode=shared -linkshared github.com/jimlawless/cfg
$ go install -buildmode=shared -linkshared git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp
```
Then finally, build the project with the option that tell to use libraries as shared and not static.
```
$ cd $GOPATH
$ go install -linkshared git.kingpenguin.tk/chteufleur/HTTPAuthentificationOverXMPP.git
```
You will find shared libraries over here :
* ``$GOROOT/pkg/linux_amd64_dynlink/libstd.so``
* ``$GOPATH/pkg/linux_amd64_dynlink/libgithub.com-jimlawless-cfg.so``
* ``$GOPATH/pkg/linux_amd64_dynlink/libgit.kingpenguin.tk-chteufleur-go-xmpp.git-src-xmpp.so``
And the binary file here :
* ``$GOPATH/bin/HTTPAuthentificationOverXMPP.git``
An example can be found [here](https://github.com/jbuberel/buildmodeshared/tree/master/gofromgo).
## Configuration
At the installation, the folder ``$XDG_CONFIG_DIRS/http-auth/`` need to be created (example ``/etc/xdg/http-auth``) where to place the configuration file named ``httpAuth.conf`` and the file lang configuration named ``messages.lang``.

View File

@ -1,9 +0,0 @@
[Unit]
Description=Provide an HTTP authentication over XMPP (https://xmpp.org/extensions/xep-0070.html)
[Service]
ExecStart=/etc/init.d/http-auth.sh start
ExecStop=/etc/init.d/http-auth.sh stop
[Install]
WantedBy=multi-user.target

View File

@ -1,53 +0,0 @@
#!/bin/sh
#######################
name="httpAuth"
dirPath="/usr/local/sbin"
#######################
sh="/bin/bash"
pidPath="/var/run/http-auth/http-auth.pid"
#######################
do_start() {
nohup $dirPath/$name &
/bin/echo $! > $pidPath
}
do_stop() {
if [ -e $pidPath ]
then
pid=$(/bin/cat $pidPath)
kill $pid
rm $pidPath
fi
}
get_status() {
if [ -e $pidPath ] ; then
pid=$(/bin/cat $pidPath) ;
/bin/echo "$name is running with PID $pid" ;
else
/bin/echo "$name is stopped" ;
fi
}
case $1 in
start)
do_start
;;
stop)
do_stop
;;
restart)
do_stop
do_start
;;
status)
get_status
;;
*)
/bin/echo "Usage: $0 {start|stop|restart|status}"
exit 2
;;
esac

93
xmpp/client.go Normal file
View File

@ -0,0 +1,93 @@
package xmpp
import (
"git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp"
"crypto/rand"
"log"
"strconv"
)
const (
dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
REPLY_UNREACHABLE = "reply_unreachable"
REPLY_DENY = "reply_deny"
REPLY_OK = "reply_ok"
TYPE_SEND_MESSAGE = "type_send_message"
TYPE_SEND_IQ = "type_send_iq"
)
type Client struct {
JID string
Method string
Domain string
Transaction string
TypeSend string
IdMap string
ChanReply chan string
}
func (client *Client) QueryClient() {
log.Printf("%sQuery JID %s", LogInfo, client.JID)
isAutoGeneratedTranctionID := false
if client.Transaction == "" {
// Random transaction ID generation
client.Transaction = TransactionID()
isAutoGeneratedTranctionID = true
}
clientJID, _ := xmpp.ParseJID(client.JID)
if clientJID.Resource == "" {
client.askViaMessage(isAutoGeneratedTranctionID)
} else {
client.askViaIQ()
}
}
func (client *Client) askViaIQ() {
stanzaID++
stanzaIDstr := strconv.Itoa(stanzaID)
m := xmpp.Iq{Type: xmpp.IQTypeGet, To: client.JID, From: jid.Domain, Id: stanzaIDstr}
confirm := &xmpp.Confirm{Id: client.Transaction, Method: client.Method, URL: client.Domain}
m.PayloadEncode(confirm)
WaitIqMessages[stanzaIDstr] = client
comp.Out <- m
client.TypeSend = TYPE_SEND_IQ
client.IdMap = stanzaIDstr
}
func (client *Client) askViaMessage(isAutoGeneratedTranctionID bool) {
m := xmpp.Message{From: jid.Domain, To: client.JID, Type: xmpp.MessageTypeNormal}
m.Thread = xmpp.SessionID()
m.Body = client.Domain + " (with method " + client.Method + ") need to validate your identity, do you agree ?"
m.Body += "\nValidation code : " + client.Transaction
if !isAutoGeneratedTranctionID {
// Send only if the transaction ID is not autogenerated
m.Body += "\nPlease check that this code is the same as on " + client.Domain
}
m.Body += "\n\nIf your client doesn't support that functionnality, please send back the validation code to confirm the request."
m.Confir = &xmpp.Confirm{Id: client.Transaction, Method: client.Method, URL: client.Domain}
log.Printf("%sSend message %v", LogInfo, m)
WaitMessageAnswers[client.Transaction] = client
comp.Out <- m
client.TypeSend = TYPE_SEND_MESSAGE
client.IdMap = client.Transaction
}
func TransactionID() string {
var bytes = make([]byte, 8)
if _, err := rand.Read(bytes); err != nil {
panic(err)
}
for k, v := range bytes {
bytes[k] = dictionary[v%byte(len(dictionary))]
}
return string(bytes)
}

View File

@ -1,97 +0,0 @@
package xmpp
import (
"git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp"
"log"
"strconv"
"strings"
)
const (
REPLY_UNREACHABLE = "reply_unreachable"
REPLY_DENY = "reply_deny"
REPLY_OK = "reply_ok"
TYPE_SEND_MESSAGE = "type_send_message"
TYPE_SEND_IQ = "type_send_iq"
TEMPLATE_DOMAIN = "_DOMAIN_"
TEMPLATE_METHOD = "_METHOD_"
TEMPLATE_VALIDATION_CODE = "_VALIDE_CODE_"
DEFAULT_MESSAGE = "_DOMAIN_ (with method _METHOD_) need to validate your identity, do you agree ?\nValidation code : _VALIDE_CODE_\nPlease check that this code is the same as on _DOMAIN_.\n\nIf your client doesn't support that functionnality, please send back the validation code to confirm the request."
)
var (
MapLangs = make(map[string]string)
)
type Confirmation struct {
JID string
Method string
Domain string
Transaction string
TypeSend string
IdMap string
ChanReply chan string
}
func (confirmation *Confirmation) SendConfirmation() {
log.Printf("%sQuery JID %s", LogInfo, confirmation.JID)
clientJID, _ := xmpp.ParseJID(confirmation.JID)
if clientJID.Resource == "" {
confirmation.askViaMessage()
} else {
confirmation.askViaIQ()
}
}
func (confirmation *Confirmation) askViaIQ() {
stanzaID++
stanzaIDstr := strconv.Itoa(stanzaID)
m := xmpp.IQ{Type: xmpp.IQTypeGet, To: confirmation.JID, From: jid.Full(), ID: stanzaIDstr}
confirm := &xmpp.Confirm{ID: confirmation.Transaction, Method: confirmation.Method, URL: confirmation.Domain}
m.PayloadEncode(confirm)
WaitIqMessages[stanzaIDstr] = confirmation
comp.Out <- m
confirmation.TypeSend = TYPE_SEND_IQ
confirmation.IdMap = stanzaIDstr
}
func (confirmation *Confirmation) askViaMessage() {
m := xmpp.Message{From: jid.Full(), To: confirmation.JID, Type: xmpp.MessageTypeNormal}
m.Thread = xmpp.SessionID()
confirmation.setBodies(&m)
m.Confirm = &xmpp.Confirm{ID: confirmation.Transaction, Method: confirmation.Method, URL: confirmation.Domain}
log.Printf("%sSend message %v", LogInfo, m)
WaitMessageAnswers[confirmation.Transaction] = confirmation
comp.Out <- m
confirmation.TypeSend = TYPE_SEND_MESSAGE
confirmation.IdMap = confirmation.Transaction
}
func (confirmation *Confirmation) setBodies(message *xmpp.Message) {
msg := DEFAULT_MESSAGE
if len(MapLangs) == 0 {
msg = strings.Replace(msg, TEMPLATE_DOMAIN, confirmation.Domain, -1)
msg = strings.Replace(msg, TEMPLATE_METHOD, confirmation.Method, -1)
msg = strings.Replace(msg, TEMPLATE_VALIDATION_CODE, confirmation.Transaction, -1)
message.Body = append(message.Body, xmpp.MessageBody{Lang: "en", Value: msg})
} else {
for key, val := range MapLangs {
msg = val
msg = strings.Replace(msg, TEMPLATE_DOMAIN, confirmation.Domain, -1)
msg = strings.Replace(msg, TEMPLATE_METHOD, confirmation.Method, -1)
msg = strings.Replace(msg, TEMPLATE_VALIDATION_CODE, confirmation.Transaction, -1)
msg = strings.Replace(msg, "\\n", "\n", -1)
msg = strings.Replace(msg, "\\r", "\r", -1)
msg = strings.Replace(msg, "\\t", "\t", -1)
message.Body = append(message.Body, xmpp.MessageBody{Lang: key, Value: msg})
}
}
}

View File

@ -8,17 +8,13 @@ import (
) )
const ( const (
LogInfo = "\t[XMPP INFO]\t" LogInfo = "\t[XMPP COMPONENT INFO]\t"
LogError = "\t[XMPP ERROR]\t" LogError = "\t[XMPP COMPONENT ERROR]\t"
LogDebug = "\t[XMPP DEBUG]\t" LogDebug = "\t[XMPP COMPONENT DEBUG]\t"
DEFAULT_SERVER_ADDRESS = "127.0.0.1"
DEFAULT_SERVER_PORT = "5347"
) )
var ( var (
Addr = "" Addr = "127.0.0.1:5347"
Port = ""
JidStr = "" JidStr = ""
Secret = "" Secret = ""
@ -32,55 +28,18 @@ var (
ChanAction = make(chan string) ChanAction = make(chan string)
WaitMessageAnswers = make(map[string]*Confirmation) WaitMessageAnswers = make(map[string]*Client)
WaitIqMessages = make(map[string]*Confirmation) WaitIqMessages = make(map[string]*Client)
Debug = true Debug = true
VerifyCertValidity = true
identity = &xmpp.DiscoIdentity{Category: "client", Type: "bot", Name: "HTTP authentication over XMPP"}
) )
func Run() { func Run() {
var addr string
var isComponent bool
log.Printf("%sRunning", LogInfo) log.Printf("%sRunning", LogInfo)
// Create stream and configure it as a component connection. // Create stream and configure it as a component connection.
jid = must(xmpp.ParseJID(JidStr)).(xmpp.JID) jid = must(xmpp.ParseJID(JidStr)).(xmpp.JID)
isComponent = jid.Node == "" stream = must(xmpp.NewStream(Addr, &xmpp.StreamConfig{LogStanzas: Debug})).(*xmpp.Stream)
comp = must(xmpp.NewComponentXMPP(stream, jid, Secret)).(*xmpp.XMPP)
if isComponent {
// component
if Addr == "" {
Addr = DEFAULT_SERVER_ADDRESS
}
if Port == "" {
Port = DEFAULT_SERVER_PORT
}
addr = Addr + ":" + Port
} else {
// client
if Addr == "" {
addrs := must(xmpp.HomeServerAddrs(jid)).([]string)
addr = addrs[0]
} else {
if Port == "" {
Port = DEFAULT_SERVER_PORT
}
addr = Addr + ":" + Port
}
}
log.Printf("%sConnecting to %s", LogInfo, addr)
stream = must(xmpp.NewStream(addr, &xmpp.StreamConfig{LogStanzas: Debug, ConnectionDomain: jid.Domain})).(*xmpp.Stream)
if isComponent {
comp = must(xmpp.NewComponentXMPP(stream, jid, Secret)).(*xmpp.XMPP)
} else {
comp = must(xmpp.NewClientXMPP(stream, jid, Secret, &xmpp.ClientConfig{InsecureSkipVerify: !VerifyCertValidity})).(*xmpp.XMPP)
comp.Out <- xmpp.Presence{}
}
mainXMPP() mainXMPP()
log.Printf("%sReach main method's end", LogInfo) log.Printf("%sReach main method's end", LogInfo)
@ -93,29 +52,24 @@ func mainXMPP() {
case *xmpp.Presence: case *xmpp.Presence:
case *xmpp.Message: case *xmpp.Message:
confirm := v.Confirm confirm := v.Confir
if confirm != nil { if confirm != nil {
confirmation := WaitMessageAnswers[confirm.ID] client := WaitMessageAnswers[confirm.Id]
processConfirm(v, confirmation) processConfirm(v, client)
} else { } else {
// If body is the confirmation id, it will be considerated as accepted. // If body is the confirmation id, it will be considerated as accepted.
// In order to be compatible with all confirmations. // In order to be compatible with all clients.
if len(v.Body) > 0 { client := WaitMessageAnswers[v.Body]
confirmation := WaitMessageAnswers[v.Body[0].Value] jidFrom, _ := xmpp.ParseJID(v.From)
jidFrom, _ := xmpp.ParseJID(v.From) if client != nil && client.JID == jidFrom.Bare() {
if confirmation != nil && confirmation.JID == jidFrom.Bare() { processConfirm(v, client)
processConfirm(v, confirmation)
}
} }
} }
case *xmpp.IQ: case *xmpp.Iq:
switch v.PayloadName().Space { switch v.PayloadName().Space {
case xmpp.NSDiscoInfo:
execDisco(v)
case xmpp.NSDiscoItems: case xmpp.NSDiscoItems:
execDisco(v) execDiscoCommand(v)
case xmpp.NSVCardTemp: case xmpp.NSVCardTemp:
reply := v.Response(xmpp.IQTypeResult) reply := v.Response(xmpp.IQTypeResult)
@ -131,17 +85,17 @@ func mainXMPP() {
case xmpp.NSHTTPAuth: case xmpp.NSHTTPAuth:
confirm := &xmpp.Confirm{} confirm := &xmpp.Confirm{}
v.PayloadDecode(confirm) v.PayloadDecode(confirm)
confirmation := WaitIqMessages[v.ID] client := WaitIqMessages[v.Id]
processConfirm(v, confirmation) processConfirm(v, client)
default: default:
// Handle reply iq that doesn't contain HTTP-Auth namespace // Handle reply iq that doesn't contain HTTP-Auth namespace
confirmation := WaitIqMessages[v.ID] client := WaitIqMessages[v.Id]
processConfirm(v, confirmation) processConfirm(v, client)
if confirmation == nil { if client == nil {
reply := v.Response(xmpp.IQTypeError) reply := v.Response(xmpp.IQTypeError)
reply.PayloadEncode(xmpp.NewError("cancel", xmpp.ErrorFeatureNotImplemented, "")) reply.PayloadEncode(xmpp.NewError("cancel", xmpp.FeatureNotImplemented, ""))
comp.Out <- reply comp.Out <- reply
} }
} }
@ -152,38 +106,38 @@ func mainXMPP() {
} }
} }
func processConfirm(x interface{}, confirmation *Confirmation) { func processConfirm(x interface{}, client *Client) {
mes, mesOK := x.(*xmpp.Message) mes, mesOK := x.(*xmpp.Message)
iq, iqOK := x.(*xmpp.IQ) iq, iqOK := x.(*xmpp.Iq)
if confirmation != nil { if client != nil {
if mesOK && mes.Error != nil { if mesOK && mes.Error != nil {
// Message error // Message error
errCondition := mes.Error.Condition() errCondition := mes.Error.Condition()
if errCondition == xmpp.ErrorServiceUnavailable { if errCondition == xmpp.ServiceUnavailable {
// unreachable // unreachable
confirmation.ChanReply <- REPLY_UNREACHABLE client.ChanReply <- REPLY_UNREACHABLE
} else { } else {
confirmation.ChanReply <- REPLY_DENY client.ChanReply <- REPLY_DENY
} }
} else if iqOK && iq.Error != nil { } else if iqOK && iq.Error != nil {
// IQ error // IQ error
errCondition := iq.Error.Condition() errCondition := iq.Error.Condition()
if errCondition == xmpp.ErrorServiceUnavailable || errCondition == xmpp.ErrorFeatureNotImplemented { if errCondition == xmpp.ServiceUnavailable || errCondition == xmpp.FeatureNotImplemented {
// send by message if client doesn't implemente it // send by message if client doesn't implemente it
confirmation.JID = strings.SplitN(confirmation.JID, "/", 2)[0] client.JID = strings.SplitN(client.JID, "/", 2)[0]
go confirmation.SendConfirmation() go client.QueryClient()
} else if errCondition == xmpp.ErrorRemoteServerNotFound { } else if errCondition == xmpp.RemoteServerNotFound {
// unreachable // unreachable
confirmation.ChanReply <- REPLY_UNREACHABLE client.ChanReply <- REPLY_UNREACHABLE
} else { } else {
confirmation.ChanReply <- REPLY_DENY client.ChanReply <- REPLY_DENY
} }
} else { } else {
// No error // No error
confirmation.ChanReply <- REPLY_OK client.ChanReply <- REPLY_OK
} }
} }
} }
@ -195,39 +149,8 @@ func must(v interface{}, err error) interface{} {
return v return v
} }
func execDisco(iq *xmpp.IQ) { func execDiscoCommand(iq *xmpp.Iq) {
log.Printf("%sDisco Feature", LogInfo) log.Printf("%sDiscovery item iq received", LogInfo)
discoInfoReceived := &xmpp.DiscoItems{}
iq.PayloadDecode(discoInfoReceived)
switch iq.PayloadName().Space {
case xmpp.NSDiscoInfo:
reply := iq.Response(xmpp.IQTypeResult)
discoInfo := &xmpp.DiscoInfo{}
discoInfo.Identity = append(discoInfo.Identity, *identity)
discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSDiscoInfo})
discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSDiscoItems})
discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSJabberClient})
reply.PayloadEncode(discoInfo)
comp.Out <- reply
case xmpp.NSDiscoItems:
if discoInfoReceived.Node == xmpp.NodeAdHocCommand {
execDiscoCommand(iq)
} else {
reply := iq.Response(xmpp.IQTypeResult)
discoItems := &xmpp.DiscoItems{}
reply.PayloadEncode(discoItems)
comp.Out <- reply
}
}
}
func execDiscoCommand(iq *xmpp.IQ) {
log.Printf("%sAd-Hoc Command", LogInfo)
reply := iq.Response(xmpp.IQTypeResult) reply := iq.Response(xmpp.IQTypeResult)
discoItem := &xmpp.DiscoItems{Node: xmpp.NodeAdHocCommand} discoItem := &xmpp.DiscoItems{Node: xmpp.NodeAdHocCommand}
reply.PayloadEncode(discoItem) reply.PayloadEncode(discoItem)