Ver Fonte

last commit

Sasan Torabkheslat há 3 anos atrás
pai
commit
0834703869
3 ficheiros alterados com 253 adições e 134 exclusões
  1. 153 127
      handler.go
  2. 84 3
      main.go
  3. 16 4
      token.go

+ 153 - 127
handler.go

@@ -3,88 +3,114 @@ package main
 import (
 	"bufio"
 	"bytes"
+	"crypto/sha256"
 	"crypto/tls"
 	"encoding/json"
 	"fmt"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/labstack/echo"
+	"golang.org/x/crypto/ssh"
 	"io/ioutil"
 	"log"
 	"net/http"
 	"net/url"
-	"os"
 	"regexp"
 	"strings"
-
-	"github.com/dgrijalva/jwt-go"
-	"github.com/labstack/echo"
-	"golang.org/x/crypto/ssh"
 )
 
 type handler struct{}
 type userInfo struct {
-	Result struct {
+	Error     interface{} `json:"error"`
+	ID        int         `json:"id"`
+	Principal string      `json:"principal"`
+	Result    struct {
 		Result struct {
-			Sshpubkeyfp      []string `json:"sshpubkeyfp"`
-			HasKeytab        bool     `json:"has_keytab"`
-			Ipasshpubkey     []string `json:"ipasshpubkey"`
 			Cn               []string `json:"cn"`
-			Krbcanonicalname []string `json:"krbcanonicalname"`
-			Krbticketflags   []string `json:"krbticketflags"`
-			MemberofGroup    []string `json:"memberof_group"`
+			Displayname      []string `json:"displayname"`
+			Dn               string   `json:"dn"`
+			Gecos            []string `json:"gecos"`
+			Gidnumber        []string `json:"gidnumber"`
+			Givenname        []string `json:"givenname"`
+			HasKeytab        bool     `json:"has_keytab"`
 			HasPassword      bool     `json:"has_password"`
 			Homedirectory    []string `json:"homedirectory"`
-			Nsaccountlock    bool     `json:"nsaccountlock"`
-			UID              []string `json:"uid"`
-			Title            []string `json:"title"`
-			Loginshell       []string `json:"loginshell"`
-			Uidnumber        []string `json:"uidnumber"`
-			Preserved        bool     `json:"preserved"`
+			Initials         []string `json:"initials"`
+			Ipauniqueid      []string `json:"ipauniqueid"`
+			Krbcanonicalname []string `json:"krbcanonicalname"`
 			Krbextradata     []struct {
 				Base64 string `json:"__base64__"`
 			} `json:"krbextradata"`
-			Mail                     []string `json:"mail"`
-			MemberofindirectHbacrule []string `json:"memberofindirect_hbacrule"`
-			Dn                       string   `json:"dn"`
-			Displayname              []string `json:"displayname"`
-			Mepmanagedentry          []string `json:"mepmanagedentry"`
-			Ipauniqueid              []string `json:"ipauniqueid"`
-			Krbloginfailedcount      []string `json:"krbloginfailedcount"`
-			Krbpwdpolicyreference    []string `json:"krbpwdpolicyreference"`
-			Krbprincipalname         []string `json:"krbprincipalname"`
-			Givenname                []string `json:"givenname"`
-			Krblastadminunlock       []struct {
-				Datetime string `json:"__datetime__"`
-			} `json:"krblastadminunlock"`
-			Krbpasswordexpiration []struct {
-				Datetime string `json:"__datetime__"`
-			} `json:"krbpasswordexpiration"`
 			Krblastfailedauth []struct {
 				Datetime string `json:"__datetime__"`
 			} `json:"krblastfailedauth"`
-			Objectclass      []string `json:"objectclass"`
-			Gidnumber        []string `json:"gidnumber"`
-			Gecos            []string `json:"gecos"`
-			Sn               []string `json:"sn"`
-			MemberofSudorule []string `json:"memberof_sudorule"`
 			Krblastpwdchange []struct {
 				Datetime string `json:"__datetime__"`
 			} `json:"krblastpwdchange"`
-			Initials []string `json:"initials"`
+			Krbloginfailedcount   []string `json:"krbloginfailedcount"`
+			Krbpasswordexpiration []struct {
+				Datetime string `json:"__datetime__"`
+			} `json:"krbpasswordexpiration"`
+			Krbprincipalname      []string `json:"krbprincipalname"`
+			Krbpwdpolicyreference []string `json:"krbpwdpolicyreference"`
+			Loginshell            []string `json:"loginshell"`
+			Mail                  []string `json:"mail"`
+			MemberofGroup         []string `json:"memberof_group"`
+			Mepmanagedentry       []string `json:"mepmanagedentry"`
+			Mobile                []string `json:"mobile"`
+			Nsaccountlock         bool     `json:"nsaccountlock"`
+			Objectclass           []string `json:"objectclass"`
+			Pager                 []string `json:"pager"`
+			Preserved             bool     `json:"preserved"`
+			Sn                    []string `json:"sn"`
+			Telephonenumber       []string `json:"telephonenumber"`
+			UID                   []string `json:"uid"`
+			Uidnumber             []string `json:"uidnumber"`
 		} `json:"result"`
-		Value   string      `json:"value"`
 		Summary interface{} `json:"summary"`
+		Value   string      `json:"value"`
 	} `json:"result"`
-	Version   string      `json:"version"`
+	Version string `json:"version"`
+}
+
+type ServiceList struct {
+	TotalCount    string `json:"TotalCount,omitempty"`
+	ActiveCount   string `json:"ActiveCount,omitempty"`
+	ActiveVMCount string `json:"ActiveVMCount,omitempty"`
+	TotalVMCount  string `json:"TotalVMCount,omitempty"`
+}
+type user_findResult struct {
 	Error     interface{} `json:"error"`
 	ID        int         `json:"id"`
 	Principal string      `json:"principal"`
+	Result    struct {
+		Count  int `json:"count"`
+		Result []struct {
+			Dn               string   `json:"dn"`
+			Gidnumber        []string `json:"gidnumber"`
+			Givenname        []string `json:"givenname"`
+			Homedirectory    []string `json:"homedirectory"`
+			Krbcanonicalname []string `json:"krbcanonicalname"`
+			Krbprincipalname []string `json:"krbprincipalname"`
+			Loginshell       []string `json:"loginshell"`
+			Mail             []string `json:"mail"`
+			Nsaccountlock    bool     `json:"nsaccountlock"`
+			Sn               []string `json:"sn"`
+			Telephonenumber  []string `json:"telephonenumber"`
+			UID              []string `json:"uid"`
+			Uidnumber        []string `json:"uidnumber"`
+		} `json:"result"`
+		Summary   string `json:"summary"`
+		Truncated bool   `json:"truncated"`
+	} `json:"result"`
+	Version string `json:"version"`
 }
 
-// Most of the code is taken from the echo guide
-// https://echo.labstack.com/cookbook/jwt
+var User = userInfo{}
+
 func (h *handler) login(c echo.Context) error {
 	username := c.FormValue("username")
 	password := c.FormValue("password")
-	_url := "https://ipa.sf.faraborddi.dc/ipa/session/login_password"
+	_url := URL + "/ipa/session/login_password"
 	method := "POST"
 	params := url.Values{}
 	params.Add("user", username)
@@ -99,17 +125,21 @@ func (h *handler) login(c echo.Context) error {
 	if err != nil {
 		fmt.Println(err)
 	}
-	req.Header.Add("Referer", "https://ipa.sf.faraborddi.dc/ipa")
+	req.Header.Add("Referer", URL+"/ipa")
 	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
 	req.Header.Add("Accept", "text/plain")
 	res, err := client.Do(req)
 	cockie := res.Cookies()
+	token := cockie[0].Raw
+	ba := basicAuth(username, password)
+	token = token + "Basic " + ba + ";"
+	//fmt.Println("Token:", token)
 	defer res.Body.Close()
-	//fmt.Println(res.StatusCode)
+	fmt.Println(res.StatusCode)
 	if res.StatusCode == 200 {
-		user := getUserInfo(cockie, username)
-		//fmt.Println(user.Result.Value)
-		tokens, err := generateTokenPair(user)
+		User = getUserInfo(token, username)
+		//fmt.Println(user.Result)
+		tokens, err := generateTokenPair(User, token)
 		if err != nil {
 			return err
 		}
@@ -119,8 +149,10 @@ func (h *handler) login(c echo.Context) error {
 
 	return echo.ErrUnauthorized
 }
-func getUserInfo(cockie []*http.Cookie, username string) userInfo {
-	url := "https://ipa.sf.faraborddi.dc/ipa/session/json"
+
+func getUserInfo(token string, username string) userInfo {
+	//fmt.Println("Checking for User: ", username)
+	url := URL + "/ipa/session/json"
 	method := "POST"
 	_json := fmt.Sprintf(`
 {
@@ -144,77 +176,48 @@ func getUserInfo(cockie []*http.Cookie, username string) userInfo {
 	}
 	client := &http.Client{Transport: tr}
 	req, err := http.NewRequest(method, url, payload)
-
+	//fmt.Println("Request URI: ",req)
 	if err != nil {
 		fmt.Println(err)
 	}
-	req.Header.Add("Referer", "https://ipa.sf.faraborddi.dc/ipa")
+	req.Header.Add("Referer", URL+"/ipa")
 	req.Header.Add("Content-Type", "application/json")
 	req.Header.Add("Accept", "text/plain")
-	req.Header.Add("Cookie", cockie[0].Raw)
+	req.Header.Add("Cookie", token)
 	res, err := client.Do(req)
-	defer res.Body.Close()
 	body, err := ioutil.ReadAll(res.Body)
+	defer res.Body.Close()
 	user := userInfo{}
-	json.Unmarshal(body, &user)
-	//fmt.Println(user.Result.Value)
-	//fmt.Println(user.Result.Result.MemberofGroup)
+	err = json.Unmarshal(body, &user)
+	if err != nil {
+		fmt.Println(err)
+	}
+	//fmt.Println("Getting Data for User: ", user.Result.Result.Uidnumber)
 	return user
 }
-
-// This is the api to refresh tokens
-// Most of the code is taken from the jwt-go package's sample codes
-// https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac
-//func (h *handler) token(c echo.Context) error {
-//	type tokenReqBody struct {
-//		RefreshToken string `json:"refresh_token"`
-//	}
-//	tokenReq := tokenReqBody{}
-//	c.Bind(&tokenReq)
-//
-//	// Parse takes the token string and a function for looking up the key.
-//	// The latter is especially useful if you use multiple keys for your application.
-//	// The standard is to use 'kid' in the head of the token to identify
-//	// which key to use, but the parsed token (head and claims) is provided
-//	// to the callback, providing flexibility.
-//	token, err := jwt.Parse(tokenReq.RefreshToken, func(token *jwt.Token) (interface{}, error) {
-//		// Don't forget to validate the alg is what you expect:
-//		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
-//			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
-//		}
-//
-//		// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
-//		return []byte("secret"), nil
-//	})
-//
-//	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
-//		// Get the user record from database or
-//		// run through your business logic to verify if the user can log in
-//		if int(claims["sub"].(float64)) == 1 {
-//
-//			newTokenPair, err := generateTokenPair()
-//			if err != nil {
-//				return err
-//			}
-//
-//			return c.JSON(http.StatusOK, newTokenPair)
-//		}
-//
-//		return echo.ErrUnauthorized
-//	}
-//
-//	return err
-//}
-
-// Most of the code is taken from the echo guide
-// https://echo.labstack.com/cookbook/jwt
-func (h *handler) private(c echo.Context) error {
+func (h *handler) token(c echo.Context) error {
 	user := c.Get("user").(*jwt.Token)
 	claims := user.Claims.(jwt.MapClaims)
-	name := claims["name"].(string)
-	return c.String(http.StatusOK, "Welcome "+name+"!")
+	_sha256 := sha256.Sum256([]byte(string(claims["name"].(string))))
+	var hashChannel_ = make(chan []byte, 1)
+	hashChannel_ <- _sha256[:]
+	token := decrypt(<-hashChannel_, claims["IPAToken"].(string))
+	username := claims["name"].(string)
+	//fmt.Println("User: ",username)
+	//fmt.Println("Token : ",token)
+	_user := getUserInfo(token, username)
+	//fmt.Println("User: ", _user.Result.Summary)
+	newtokens, err := generateTokenPair(_user, token)
+	if err != nil {
+		return err
+	}
+	return c.JSON(http.StatusOK, newtokens)
 }
+func IntStringParser(str string) []string {
 
+	re := regexp.MustCompile(`\s{1,}`)
+	return strings.Split(re.ReplaceAllString(str, ","), ",")
+}
 func Connect(user, pass, host string, cmd string) bytes.Buffer {
 	cipher := ssh.Config{
 		Ciphers: []string{"aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
@@ -260,46 +263,69 @@ func Connect(user, pass, host string, cmd string) bytes.Buffer {
 }
 
 func (h *handler) findMAC(c echo.Context) error {
-	user := c.Get("user").(*jwt.Token)
-	claims := user.Claims.(jwt.MapClaims)
-	name := claims["name"].(string)
-	result1 := Connect("rancid", "JDACy6wK*yW%meQ", os.Args[1], os.Args[2])
-	intDesc := os.Args[3]
+	//user := c.Get("user").(*jwt.Token)
+	//claims := user.Claims.(jwt.MapClaims)
+	//name := claims["name"].(string)
+	HOSTNAMEPORT := c.FormValue("HOSTNAMEPORT")
+	SERVERURI := c.FormValue("SERVERURI")
+	result1 := Connect("rancid", "JDACy6wK*yW%meQ", HOSTNAMEPORT, "sh int status")
+	intDesc := SERVERURI
 	var IntString string
 	scanner := bufio.NewScanner(&result1)
 	var IntName string
 	var IntMAC string
 	// var IntName, IntStatus, IntVLAN, IntDuplex, IntSpeed, IntType string
 	for scanner.Scan() {
-		// fmt.Println("Text: " + scanner.Text())
 		if strings.Contains(scanner.Text(), intDesc) {
-			// fmt.Println("Text: " + scanner.Text())
 			IntString = scanner.Text()
 			IntName = IntStringParser(IntString)[0]
 			break
 		}
 	}
-	// fmt.Println(IntName)
-	result1 = Connect("rancid", "JDACy6wK*yW%meQ", os.Args[1], "sh mac address-table int "+IntName)
+	result1 = Connect("rancid", "JDACy6wK*yW%meQ", HOSTNAMEPORT, "sh mac address-table int "+IntName)
 	scanner = bufio.NewScanner(&result1)
 	for scanner.Scan() {
-		// fmt.Println("Text: " + scanner.Text())
 		if strings.Contains(scanner.Text(), IntName) {
 			if strings.Contains(scanner.Text(), "mac") {
 				continue
 			}
-			// fmt.Println(scanner.Text())
+			//fmt.Println(scanner.Text())
 			IntString = scanner.Text()
+			//fmt.Println("len: ", len(IntStringParser(IntString)) )
+			if len(IntStringParser(IntString)) < 3 {
+				id, code := uuidgen("findMAC")
+				resp := _response{
+					Origin:  "findMAC",
+					Message: "",
+					Code:    code,
+					Uuid:    id,
+				}
+				return c.JSON(http.StatusNotFound, resp)
+			}
 			IntMAC = IntStringParser(IntString)[2]
-			// fmt.Println(IntMAC)
-			// break
 		}
 	}
-	fmt.Println(IntMAC)
-	return c.String(http.StatusOK, "Welcome "+IntMAC+"!")
+	//fmt.Println(IntMAC)
+	//return c.String(http.StatusOK, "Welcome "+IntMAC+"!")
+	id, code := uuidgen("findMAC")
+
+	resp := _response{
+		Origin:  "findMAC",
+		Message: macParser(IntMAC),
+		Code:    code,
+		Uuid:    id,
+	}
+	return c.JSON(http.StatusOK, resp)
 }
-func IntStringParser(str string) []string {
 
-	re := regexp.MustCompile(`\s{1,}`)
-	return strings.Split(re.ReplaceAllString(str, ","), ",")
+func macParser(str string) string {
+	if len(str) < 12 {
+		return "ERROR"
+	}
+	_str := strings.Split(str, ".")
+	__str := _str[0] + _str[1] + _str[2]
+
+	___str := __str[0:2] + ":" + __str[2:4] + ":" + __str[4:6] + ":" + __str[6:8] + ":" + __str[8:10] + ":" + __str[10:12]
+
+	return strings.ToUpper(___str)
 }

+ 84 - 3
main.go

@@ -1,7 +1,13 @@
 package main
 
 import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"encoding/base64"
 	"fmt"
+	"github.com/google/uuid"
+	"io"
 	"log"
 	"log/syslog"
 	"net/http"
@@ -26,6 +32,8 @@ func audit(txt string) {
 
 var RealIP string
 
+var URL string = "https://ipa.sf.faraborddi.dc"
+
 func extractIP(next echo.HandlerFunc) echo.HandlerFunc {
 	return func(c echo.Context) error {
 		RealIP = c.RealIP()
@@ -33,6 +41,18 @@ func extractIP(next echo.HandlerFunc) echo.HandlerFunc {
 		return next(c)
 	}
 }
+func basicAuth(username, password string) string {
+	auth := username + "@IPA:" + password
+	return base64.StdEncoding.EncodeToString([]byte(auth))
+}
+
+type _response struct {
+	Message string `json:"message"`
+	Origin  string `json:"origin"`
+	Code    int    `json:"code"`
+	Uuid    string `json:"uuid"`
+}
+
 func main() {
 	if len(os.Args) != 3 {
 		fmt.Println("Wrong Usage:\n\t ./CMD IP Port")
@@ -49,12 +69,73 @@ func main() {
 	h := &handler{}
 	echoHandler.POST("/login", h.login)
 
-	echoHandler.GET("/private", h.private, isLoggedIn)
-	echoHandler.GET("/findMAC", h.findMAC, isLoggedIn)
+	//echoHandler.GET("/private", h.private, isLoggedIn)
+	echoHandler.POST("/findMAC", h.findMAC, isLoggedIn, isAdmin)
 
-	echoHandler.GET("/admin", h.private, isLoggedIn, isAdmin)
+	//echoHandler.GET("/admin", h.private, isLoggedIn, isAdmin)
 
 	//echoHandler.POST("/token", h.token)
 
 	echoHandler.Logger.Fatal(echoHandler.Start(os.Args[1] + ":" + os.Args[2]))
 }
+func uuidgen(resource string) (string, int) {
+	id := uuid.New()
+	if len(resource) < 3 {
+		return "resource name should be at least 3 characters!", 1001
+	}
+
+	//fmt.Println("uuidGen for ", id, " at ", resource)
+
+	return fmt.Sprintf("%s", id), 1000
+}
+func encrypt(key []byte, text string) string {
+	// key := []byte(keyText)
+	//fmt.Println("Encrypt by: ", key)
+	plaintext := []byte(text)
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		//panic(err)
+		fmt.Println("encrypt got error")
+		return ""
+	}
+	// The IV needs to be unique, but not secure. Therefore it's common to
+	// include it at the beginning of the ciphertext.
+	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+	iv := ciphertext[:aes.BlockSize]
+	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+		fmt.Println("encrypt got error")
+		return ""
+	}
+
+	stream := cipher.NewCFBEncrypter(block, iv)
+	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+
+	// convert to base64
+	return base64.URLEncoding.EncodeToString(ciphertext)
+}
+func decrypt(key []byte, cryptoText string) string {
+	ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)
+	//fmt.Println("Decrypt by: ", key)
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		fmt.Println("encrypt got error")
+		return ""
+		//panic(err)
+	}
+
+	// The IV needs to be unique, but not secure. Therefore it's common to
+	// include it at the beginning of the ciphertext.
+	if len(ciphertext) < aes.BlockSize {
+		fmt.Println("encrypt got error")
+		return ""
+		//panic("ciphertext too short")
+	}
+	iv := ciphertext[:aes.BlockSize]
+	ciphertext = ciphertext[aes.BlockSize:]
+
+	stream := cipher.NewCFBDecrypter(block, iv)
+
+	// XORKeyStream can work in-place if the two arguments are the same.
+	stream.XORKeyStream(ciphertext, ciphertext)
+	return fmt.Sprintf("%s", ciphertext)
+}

+ 16 - 4
token.go

@@ -1,12 +1,14 @@
 package main
 
 import (
+	"crypto/sha256"
 	"time"
 
 	"github.com/dgrijalva/jwt-go"
 )
 
-func generateTokenPair(user userInfo) (map[string]string, error) {
+func generateTokenPair(user userInfo, cockieStr string) (map[string]string, error) {
+	//fmt.Println(user.Result.Result.UID)
 	// Create token
 	token := jwt.New(jwt.SigningMethodHS256)
 
@@ -16,12 +18,20 @@ func generateTokenPair(user userInfo) (map[string]string, error) {
 	claims := token.Claims.(jwt.MapClaims)
 	claims["admin"] = false
 	for _, v := range user.Result.Result.MemberofGroup {
-		if v == "admins" {
+		if v == "usermodifier" || v == "admins" {
 			claims["admin"] = true
 		}
 	}
 	claims["sub"] = 1
-	claims["name"] = user.Result.Value
+	claims["name"] = user.Result.Result.UID[0]
+	claims["fullname"] = user.Result.Result.Displayname
+	claims["IPAUid"] = user.Result.Result.Uidnumber[0]
+	sha256 := sha256.Sum256([]byte(user.Result.Result.UID[0]))
+	var hashChannel = make(chan []byte, 1)
+	hashChannel <- sha256[:]
+	claims["IPAToken"] = encrypt(<-hashChannel, cockieStr)
+	claims["memberof"] = user.Result.Result.MemberofGroup
+	claims["mail"] = user.Result.Result.Mail
 	claims["exp"] = time.Now().Add(time.Minute * 15).Unix()
 
 	// Generate encoded token and send it as response.
@@ -34,7 +44,9 @@ func generateTokenPair(user userInfo) (map[string]string, error) {
 	refreshToken := jwt.New(jwt.SigningMethodHS256)
 	rtClaims := refreshToken.Claims.(jwt.MapClaims)
 	rtClaims["sub"] = 1
-	rtClaims["exp"] = time.Now().Add(time.Hour * 24).Unix()
+	rtClaims["IPAToken"] = claims["IPAToken"]
+	rtClaims["name"] = claims["name"]
+	rtClaims["exp"] = time.Now().Add(time.Hour * 1).Unix()
 
 	rt, err := refreshToken.SignedString([]byte("secret"))
 	if err != nil {