-
Notifications
You must be signed in to change notification settings - Fork 7
Add linked token resolution #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
fc58a69
25a6c4b
2affb46
75ed2e3
322790e
76e5289
b4e2d29
d1f1c41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/* | ||
Package websspi provides middleware to require with Windows Integrated Authentication. | ||
*/ | ||
package websspi |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,7 +63,8 @@ func (c contextKey) String() string { | |
} | ||
|
||
var ( | ||
UserInfoKey = contextKey("UserInfo") | ||
UserInfoKey = contextKey("UserInfo") | ||
LinkedTokenUserInfoKey = contextKey("LinkedTokenUserInfo") | ||
) | ||
|
||
// The Authenticator type provides middleware methods for authentication of http requests. | ||
|
@@ -341,19 +342,113 @@ func (a *Authenticator) GetUsername(context *CtxtHandle) (username string, err e | |
return | ||
} | ||
|
||
// GetGroups returns the groups assosiated with the specified security context | ||
func (a *Authenticator) GetGroups(context *CtxtHandle) (groups []string, err error) { | ||
// GetAccessToken returns the access token of a context handle. | ||
func (a *Authenticator) GetAccessToken(context *CtxtHandle) (t syscall.Token, err error) { | ||
var token SecPkgContext_AccessToken | ||
status := a.Config.authAPI.QueryContextAttributes(context, SECPKG_ATTR_ACCESS_TOKEN, (*byte)(unsafe.Pointer(&token))) | ||
if status != SEC_E_OK { | ||
err = fmt.Errorf("QueryContextAttributes failed with status 0x%x", status) | ||
return | ||
} | ||
return syscall.Token(token.AccessToken), err | ||
} | ||
|
||
// GetLinkedUserInfo returns the user info of a linked token e.g. the full token when using the UAC | ||
func (a *Authenticator) GetLinkedUserInfo(context *CtxtHandle) (u *UserInfo, err error) { | ||
var token syscall.Token | ||
token, err = a.GetAccessToken(context) | ||
if err != nil { | ||
return | ||
} | ||
|
||
linkedUserInfo := TokenLinkedToken{} | ||
var usedMemory uint32 | ||
|
||
err = a.Config.authAPI.GetTokenInformation( | ||
token, | ||
uint32(syscall.TokenLinkedToken), | ||
(*byte)(unsafe.Pointer(&linkedUserInfo)), | ||
uint32(reflect.TypeOf(linkedUserInfo).Size()), | ||
&usedMemory, | ||
) | ||
if err != nil { | ||
return | ||
} | ||
|
||
defer syscall.CloseHandle(linkedUserInfo.LinkedToken) | ||
linkedToken := syscall.Token(linkedUserInfo.LinkedToken) | ||
|
||
// The buffer will also store the SID, therefore more than sizeof(TokenUser) bytes are required. | ||
err = a.Config.authAPI.GetTokenInformation( | ||
linkedToken, | ||
uint32(syscall.TokenUser), | ||
nil, | ||
0, | ||
&usedMemory, | ||
) | ||
if err != syscall.ERROR_INSUFFICIENT_BUFFER { | ||
return | ||
} | ||
|
||
buffer := make([]byte, int(usedMemory)) | ||
err = a.Config.authAPI.GetTokenInformation( | ||
linkedToken, | ||
uint32(syscall.TokenUser), | ||
&buffer[0], | ||
usedMemory, | ||
&usedMemory, | ||
) | ||
|
||
tokenuser := (*TokenUser)(unsafe.Pointer(&buffer[0])) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't really matter, but we could first check the value of err, before setting the value of the tokenuser variable |
||
|
||
if err != nil { | ||
return | ||
} | ||
|
||
var stringsid string | ||
stringsid, err = tokenuser.User.Sid.String() | ||
if err != nil { | ||
return | ||
|
||
} | ||
|
||
var lookedup *user.User | ||
lookedup, err = user.LookupId(stringsid) | ||
if err != nil { | ||
return | ||
} | ||
|
||
u = &UserInfo{} | ||
u.Username = lookedup.Username | ||
|
||
if a.Config.EnumerateGroups { | ||
if a.Config.ServerName == "" { | ||
u.Groups, err = a.GetGroupsFromToken(linkedToken) | ||
} else { | ||
u.Groups, err = a.GetUserGroups(u.Username) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure about this, but if
|
||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
// GetGroups returns the groups assosiated with the specified security context | ||
func (a *Authenticator) GetGroups(context *CtxtHandle) (groups []string, err error) { | ||
var token syscall.Token | ||
token, err = a.GetAccessToken(context) | ||
if err != nil { | ||
return | ||
} | ||
return a.GetGroupsFromToken(token) | ||
} | ||
|
||
// GetGroupsFromToken returns the active groups of a Windows token | ||
func (a *Authenticator) GetGroupsFromToken(token syscall.Token) (groups []string, err error) { | ||
var requiredMemory uint32 | ||
|
||
// 1. Get buffer size | ||
ec := a.Config.authAPI.GetTokenInformation( | ||
syscall.Token(token.AccessToken), | ||
syscall.Token(token), | ||
syscall.TokenGroups, | ||
nil, 0, &requiredMemory, | ||
) | ||
|
@@ -366,7 +461,7 @@ func (a *Authenticator) GetGroups(context *CtxtHandle) (groups []string, err err | |
tokenInformation := make([]byte, requiredMemory) | ||
// 2. Get data | ||
ec = a.Config.authAPI.GetTokenInformation( | ||
syscall.Token(token.AccessToken), | ||
syscall.Token(token), | ||
syscall.TokenGroups, | ||
&tokenInformation[0], uint32(len(tokenInformation)), &requiredMemory, | ||
) | ||
|
@@ -480,6 +575,13 @@ func (a *Authenticator) GetUserInfo(context *CtxtHandle) (*UserInfo, error) { | |
} | ||
} | ||
|
||
if a.Config.ResolveLinked { | ||
info.linked, err = a.GetLinkedUserInfo(context) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return &info, nil | ||
} | ||
|
||
|
@@ -664,6 +766,9 @@ func (a *Authenticator) WithAuth(next http.Handler) http.Handler { | |
log.Print("Authenticated\n") | ||
// Add the UserInfo value to the reqest's context | ||
r = r.WithContext(context.WithValue(r.Context(), UserInfoKey, user)) | ||
if user.linked != nil { | ||
r = r.WithContext(context.WithValue(r.Context(), LinkedTokenUserInfoKey, user.linked)) | ||
} | ||
// and to the request header with key Config.AuthUserKey | ||
if a.Config.AuthUserKey != "" { | ||
r.Header.Set(a.Config.AuthUserKey, user.Username) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be we should extend a bit the comment in the doc.go file. Something like that should do: